react useContext
一、什么是useContext
在 React class 式中父组件向子组件传递参数可以通过 props ,context。但是在函数式组件中需要向多层组件传递数据时,此时就可以使用 useContext/
二、useContext的作用
1.useContext可以帮助我们跨越组件层级直接传递变量,实现数据共享。
- 这里要注意的是,很多同学觉得可以使用useContext结合useReducer来替代redux,其实两者的作用是不同的。
- useContext:解决组件间传值的问题。
- redux:统一管理应用状态。
- 所以,我们可以使用useContext结合useReducer来模拟一个小型redux场景,而无法替代redux
2.Context的作用就是对它所包含的组件树提供全局共享数据的一种技术。
三、代码示例
import React, { useState } from "react"; let Theme = React.createContext("red"); const Inner = () => { let color = React.useContext(Theme); return <div><span style={{color: color}}>文字</span></div> } const Con = () => { return <Inner /> } const Side = () => { console.log("side") return <><span>side</span></> } export default function App() { const [color, setColor] = useState("red") return ( <div className="App"> <button onClick={() => setColor("red")}>红色</button> <button onClick={() => setColor("green")}>绿色</button> <Theme.Provider value={color}> <Con /> <Side /> </Theme.Provider> </div> ); }
在上面的例子中,我通过在顶层创建 Provider 组件,深层次的子组件通过 useContext 取得 值,这样使得在父组件修改 context 的值之后,子组件也能取得相对应的值,并重新渲染。
需要注意的是,useContext 函数接收的参数对象是 context 自身。
存在于Provider 组件下的子组件在context 的值发生变化后,都会重新渲染,这样可能会引发一些性能问题,我们可以通过 memo 去避免一些不必要的重渲染。
const Side = memo(() => { console.log("side") return <><span>side</span></> })
Context 是通过新旧值检测来确定变化,使用了与Object.is
相同的算法。
如果传给Context 是个字面对象的话,有可能使得每次值的检测都会使得子组件重新渲染。
import React, { useState, memo } from "react"; let Theme = React.createContext({color:"red"}); const Inner = memo(() => { let theme = React.useContext(Theme); console.log("Inner") return <div><span style={{color: theme.color}}>文字</span></div> }) const Con = () => { return <Inner /> } const Side = memo(() => { console.log("side") return <><span>side</span></> }) export default function App() { const [color, setColor] = useState("red") const [count, setCount] = useState(0); return ( <div className="App"> <div> {count} <button onClick={() => setCount((v) => v + 1 )}>count</button> </div> <button onClick={() => setColor("red")}>红色</button> <button onClick={() => setColor("green")}>绿色</button> <Theme.Provider value={{color}}> <Con /> <Side /> </Theme.Provider> </div> ); }
上述的代码中,因为父组件 App 重新渲染时提供给Conext 的都是一个新的字面量对象,导致Inner 组件会在父组件的每次重渲染中去跟着重新渲染,这实际上是没有必要的。
import React, { useState, memo } from "react"; let Theme = React.createContext({color:"red"}); const Inner = memo(() => { let theme = React.useContext(Theme); console.log("Inner") return <div><span style={{color: theme.color}}>文字</span></div> }) const Con = () => { return <Inner /> } const Side = memo(() => { return <><span>side</span></> }) export default function App() { const [theme, setTheme] = useState({color: "red"}) const [count, setCount] = useState(0); return ( <div className="App"> <div> {count} <button onClick={() => setCount((v) => v + 1 )}>count</button> </div> <button onClick={() => setTheme({color: "red"})}>红色</button> <button onClick={() => setTheme({color: "green"})}>绿色</button> <Theme.Provider value={theme}> <Con /> <Side /> </Theme.Provider> </div> ); }
通过上面的修改后,使得 Inner 避免了不必要的渲染。