「这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战」
导出一个createContext方法
export function createContext(defaultValue) {
}
复制代码
React.js文件导出了一个创建上下文的方法,这个上下文用来连接生产者和消费者。生产者只需要将生产的产品丢给上下文,消费者需要时会从上下文获取产品。这样生产者和消费者之间解耦,生产者不用等待消费者消费,就可以把生产的产品交付出去。
循环引用(震惊)
export function createContext(defaultValue) {
const context = {
$$typeof: REACT_CONTEXT_TYPE,
_currentValue: defaultValue,
_currentValue2: defaultValue,
_threadCount: 0,
Provider:null,
Consumer:null,
};
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
context.Consumer = context;
return context;
}
复制代码
如下图所示,上述代码构成了一个循环引用,震惊!!!(react大神居然写出了容易导致内存泄漏的循环引用)
上面代码中,首先定义了一个上下文对象。
- 生产者通过
_context
属性持有上下文对象。 - 消费者引用上下文对象,而上下文对象又引用消费者对象,消费者对象引用上下问对象...
- 生产者同样也产生了一个循环引用。
这样就成了永远也讲不完的故事了。
从前有座山,山里有座庙,庙里有个老和尚......
简直没完没了。
笔者的猜测
循环引用可以让context
变量以及Provider
和Consumer
在一个全局上下文中不被gc回收,一旦被创建,就可以保持稳定的存在。好,react想让生产者和消费者稳定地存在在当前运行内存下。我们先记下来。风险是,如果createContext
被多次调用,将会有很多内存空间得不到回收,最后造成内存溢出。
ReactContext的使用示例
const EditableContext = React.createContext<FormInstance<any> | null>(null);
<EditableContext.Provider value={form}> <tr {...props} /> </EditableContext.Provider>
const form = useContext(EditableContext)!;
复制代码
这里,首先创建一个上下文对象。生产者生产了一个表单对象,然后消费者通过钩子函数消费了这个表单对象。React中可以创建函数式组件,EditableContext.Provider。 React组件其实是一种数据结构,上下文的消费者和生产者都是无标签对象,jsx在解析时不会渲染它们,而只会渲染它们的children属性。它们的作用仅仅是创建上下文,共享数据。一般,生产者在顶层创建,消费者在里层消费者生产的产品。
一般ReactContext放在组件的顶层,其下有很多的消费者。这样,createContext不会被过多调用。而且在运行过程中不会被当成垃圾回收。 这样,我们可以放行稳定的消费生产者生产的产品,比如生产的表单对象。
感谢阅读,ReactContext毕竟有造成内存泄漏的风险,因而,我们只在必须要用到的地方去使用它。在React组件的顶层去使用它是一个好的实践。比如设置一些通用的全局属性,背景色等等。