从前有座山之react源码解读之ReactContext

「这是我参与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大神居然写出了容易导致内存泄漏的循环引用) image.png

上面代码中,首先定义了一个上下文对象。

  • 生产者通过_context属性持有上下文对象。
  • 消费者引用上下文对象,而上下文对象又引用消费者对象,消费者对象引用上下问对象...
  • 生产者同样也产生了一个循环引用。

这样就成了永远也讲不完的故事了。

从前有座山,山里有座庙,庙里有个老和尚......

简直没完没了。

笔者的猜测

循环引用可以让context变量以及ProviderConsumer在一个全局上下文中不被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组件的顶层去使用它是一个好的实践。比如设置一些通用的全局属性,背景色等等。

猜你喜欢

转载自juejin.im/post/7034899177205137438