一、React.createContext
API 文档地址:zh-hans.reactjs.org/docs/contex…
从 API 名字就可以看出, createContext 能够创建一个 React 的 上下文(context),然后订阅了这个上下文的组件中,可以拿到上下文中提供的数据或者其他信息。
基本的使用方法:
const MyContext = React.createContext(defaultValue)
其中 defaultValue 是传入的默认值。
如果要使用创建的上下文,需要通过 Context.Provider 最外层包装组件,并且需要显示的通过 <MyContext.Provider value={{xx:xx}}> 的方式传入 value,指定 context 要对外暴露的信息。
子组件在匹配过程中只会匹配最新的 Provider,也就是说如果有下面三个组件:ContextA.Provider->A->ContexB.Provider->B->C
如果 ContextA 和 ContextB 提供了相同的方法,则 C 组件只会选择 ContextB 提供的方法。
为什么要默认值?
如果匹配不到最新的 Provider 则会使用默认值,默认值一般只有在对组件进行单元测试(组件并未嵌入到父组件中)的时候,比较有用。
二、使用 useContext 获取上下文
useContext 文档地址:zh-hans.reactjs.org/docs/hooks-…
通过 React.createContext 创建出来的上下文,在子组件中可以通过 useContext 这个 Hook 获取 Provider 提供的内容
const {funcName} = useContext(MyContext);
从上面代码可以发现,useContext 需要将 MyContext 这个 Context 实例传入,不是字符串,就是实例本身。
这种用法会存在一个比较尴尬的地方,父子组件不在一个目录中,如何共享 MyContext 这个 Context 实例呢?
一般这种情况下,我会通过 Context Manager 统一管理上下文的实例,然后通过 export 将实例导出,在子组件中在将实例 import 进来。
三、createContext 和 useContext 结合使用实现方法共享
举个实际的例子:子组件中修改父组件的 state
一般的做法是将父组件的方法比如 setXXX 通过 props 的方式传给子组件,而一旦子组件多层级的话,就要层层透传。
使用 Context 的方式则可以免去这种层层透传
1、context-manager.js
创建一个上下文管理的组件,用来统一导出 Context 实例
import React from 'react';
export const MyContext = React.createContext(null);
2、父组件 Provider 提供上下文 value
下面代码中,父组件引入了实例,并且通过 MyContext.Provider 将父组件包装,并且通过 Provider.value 将方法提供出去。
下面的实例提供了三个 state 操作方法:
- setStep
- setCount
- setNumber
以及一个副作用方法:
- fetchData
子组件 Child 接受的 props 只有三个 state 的值 step/number/count。
import React, { useState } from 'react';
import Child from './Child';
import { MyContext } from './context-manager';
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
})
});
}
export default (props = {}) => {
const [step, setStep] = useState(0);
const [count, setCount] = useState(0);
const [number, setNumber] = useState(0);
return (
<MyContext.Provider value={{ setStep, setCount, setNumber, fetchData }}>
<Child step={step} number={number} count={count} />
</MyContext.Provider>
);