一、前言
Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。使用 context, 我们可以避免通过中间元素传递 props。
const value = useContext(MyContext);
接收一个 context 对象(React.createContext
的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider>
的 value prop 决定。
当组件上层最近的 <MyContext.Provider>
更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。即使祖先使用 React.memo
或 shouldComponentUpdate
,也会在组件本身使用 useContext
时重新渲染。调用了 useContext
的组件总会在 context 值变化时重新渲染
别忘记 useContext 的参数必须是 context 对象本身:
- 正确:
useContext(MyContext)
- 错误:
useContext(MyContext.Consumer)
- 错误:
useContext(MyContext.Provider)
二、使用
创建一个 Context 对象 (React.createContext)
当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。
只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效。此默认值有助于在不使用 Provider 包装组件的情况下对组件进行测试。注意:将 undefined 传递给 Provider 的 value 时,消费组件的 defaultValue 不会生效。
// MyContext.ts
import { createContext } from "react";
export const MyContext = createContext(null);
包裹 Context.Provider 组件
每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。
Provider 接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。
// index.ts
import { MyContext } from './MyContext';
import { MyContextComponent } from './MyContextComponent';
const initialData = {
name: 'index',
age:18,
}
const index = () => {
return(
<MyContext.Provider value={ initialData }>
<MyContextComponent />
</MyContext.Provider>
)
}
export default index;
嵌套消费组件层级 (未订阅 MyContext 变化)
// MyContextComponent.ts
import { MyContext1 } from './MyContext1';
import { MyContext2 } from './MyContext2';
const initialData = {
name: 'index',
age:18,
}
const MyContextComponent = () => {
return(
<>
<MyContext1 />
<MyContext2 />
</>
)
}
export default MyContextComponent;
组件中订阅 MyContext
// MyContext1.ts
import { ReducerContext } from './MyContext';
const MyContext1 = () => {
const MyContext1Context = useContext(MyContext)
return(
<div>
订阅indexname:
{MyContext1Context.name}
</div>
)
}
export default MyContext1;
三、在嵌套组件中更新 Context
若在嵌套组件中更新 上面例子中的 MyContext,则将传入的对象使用 useState函数更新:
// index.ts
import { ReducerContext } from './MyContext';
import { MyContextComponent } from './MyContextComponent';
const initialData = {
name: 'index',
age:18,
}
const index = () => {
const [indexData, setIndexData] = useState(initialData)
return(
<MyContext.Provider value={ setIndexData }>
<MyContextComponent />
</MyContext.Provider>
)
}
export default index;
// MyContext1.ts
import { ReducerContext } from './MyContext';
const MyContext1 = () => {
const MyContext1Context = useContext(MyContext)
return(
<button onClick={() => MyContext1Context(10)}>修改</button>
)
}
export default MyContext1;
四、useState + useContext 结合实现 redux 数据管理效果
在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。
useReducer 替代 useState ,加上 useContext可以实现类似 redux 的数据管理效果。
可参考:结合 useContext 和 useReducer ( redux 效果)