使用场景:当需要给一个嵌套层级较深的一个组件传值或者你就不想使用props传值的时候你可以使用react.createContext
react.createContext的使用方法
1、使用Provider, Consumer
const {Provider, Consumer} = React.createContext(defaultValue);
const ProviderCom = <Provider value={form}>
<Child {...props} />
</Provider>
const Child = <Consumer>
{form => (
... //这里可以接收到ProviderCom传递的form参数
)}
</Consumer>
2、使用Provider和useContext
const LogStateContext = React.createContext(defaultValue);
const [logs, setLogs] = useState([]);
const ProviderCom = <LogStateContext.Provider value={logs}>
{children}
</LogStateContext.Provider>
const LogsPanel = () => {
const logs = useContext(LogStateContext); //使用useContext可以解析出LogStateContext.Provider传递的logs
return logs.map((log, index) => <p key={index}>{log}</p>);
}
react.createContext源码分析
function createContext(defaultValue, calculateChangedBits) {
if (calculateChangedBits === undefined) {
calculateChangedBits = null;
} else {
{
!(calculateChangedBits === null || typeof calculateChangedBits === 'function') ? warningWithoutStack$1(false, 'createContext: Expected the optional second argument to be a ' + 'function. Instead received: %s', calculateChangedBits) : void 0;
}
}
var context = {
$$typeof: REACT_CONTEXT_TYPE, //context的$$typeof在createElement中的type中的type对象中存储
_calculateChangedBits: calculateChangedBits,//计算新老context变化
//_currentValue和_currentValue2作用一样,只是作用平台不同
_currentValue: defaultValue, //Provider的value属性
_currentValue2: defaultValue,
_threadCount: 0, //用来追踪context的并发渲染器数量
// These are circular
Provider: null, //提供组件
Consumer: null //应用组件
};
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context
};
var hasWarnedAboutUsingNestedContextConsumers = false;
var hasWarnedAboutUsingConsumerProvider = false;
{
// A separate object, but proxies back to the original context object for
// backwards compatibility. It has a different $$typeof, so we can properly
// warn for the incorrect usage of Context as a Consumer.
var Consumer = {
$$typeof: REACT_CONTEXT_TYPE,
_context: context,
_calculateChangedBits: context._calculateChangedBits
}; // $FlowFixMe: Flow complains about not setting a value, which is intentional here
Object.defineProperties(Consumer, {
Provider: {
get: function () {
if (!hasWarnedAboutUsingConsumerProvider) {
hasWarnedAboutUsingConsumerProvider = true;
warning$1(false, 'Rendering <Context.Consumer.Provider> is not supported and will be removed in ' + 'a future major release. Did you mean to render <Context.Provider> instead?');
}
return context.Provider;
},
set: function (_Provider) {
context.Provider = _Provider;
}
},
_currentValue: {
get: function () {
return context._currentValue;
},
set: function (_currentValue) {
context._currentValue = _currentValue;
}
},
_currentValue2: {
get: function () {
return context._currentValue2;
},
set: function (_currentValue2) {
context._currentValue2 = _currentValue2;
}
},
_threadCount: {
get: function () {
return context._threadCount;
},
set: function (_threadCount) {
context._threadCount = _threadCount;
}
},
Consumer: {
get: function () {
if (!hasWarnedAboutUsingNestedContextConsumers) {
hasWarnedAboutUsingNestedContextConsumers = true;
warning$1(false, 'Rendering <Context.Consumer.Consumer> is not supported and will be removed in ' + 'a future major release. Did you mean to render <Context.Consumer> instead?');
}
return context.Consumer;
}
}
}); // $FlowFixMe: Flow complains about missing properties because it doesn't understand defineProperty
context.Consumer = Consumer;
}
{
context._currentRenderer = null;
context._currentRenderer2 = null;
}
return context;
}
- 在context设置Provider和Consumer属性值
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context
};
表示给context的provider设置属性值 如果我们需要设置当前Provider上的value值 则直接使用context.Provider._context._currentValue进行设置
var Consumer = {
$$typeof: REACT_CONTEXT_TYPE,
_context: context,
_calculateChangedBits: context._calculateChangedBits
};
context.Consumer = Consumer;
表示给context设置Consumer设置属性值 如果我们需要获取当前通过Provider上的value值 则直接通过自身的Consumer组件的context.Consumer._context._currentValue获取value值
我们看到 createContext ,他接收的是一个 defaultValue ,还有一个是 calculateChangedBits 。是一个方法,是用来计算新老 context 的一个变化的。方法里面看到他声明了一个 context 对象,这个对象跟之前看的 ReactElement 非常的像。也有一个 typeof 跟 ReactElement 的 $$typeof 是不一样的。还有两个属性 _currentValue , _currentValue2 这两个属性是一样的,只是用到的地方不一样。 _currentValue 这个 value 是用来记录 Provider 里面的这个 value 。 他有变化的情况下就会更新到这个 _currentValue 。这就是用来记录最新的 context 的值的, 下面会有个 Provider 和 Consumer 。
下面有个 context.Provider ,他有个属性 _context ,这个属性会指向这个 context。context.Consumer = context。 也就是说 Consumer 是等于自己的。
到这我们知道 Consumer 就是指向这个对象的时候,可以猜到,在 Consumer 进行渲染的时候,他要去获取这个 value 怎么办呢,他只要从自己本身上面 _currentValue 就可以拿到最新的 context 的 value 。然后再调用那个方法把他传进去。这就是基本的实现原理,真正的实现没有这么简单。
这里面的 typeof。而是这里面的 $$typeof 是指 ReactElement 里面的 type.