文章目录

  • Context
    • Context 相关用法
    • Context 原理
    • 总结

Context

React提供了 Context 上下文模式,为了解决 props 每层都需要传递的问题,即Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法

注意:提供者一定要是消费者的某一层父级

Context 相关用法

v16.3.0后,context api 正式发布

createContext 创建出一个 context 上下文对象,context 对象提供两个组件,ProviderConsumer作为新的提供者和消费者,这种 context 模式,更便捷的传递 context

1、React.createContext

const ThemeContext = React.createContext(null) //创建context对象
const ThemeProvider = ThemeContext.Provider  //提供者
const ThemeConsumer = ThemeContext.Consumer // 订阅消费者
  • createContext 接受一个参数,作为初始化 context 的内容,返回一个Context 对象
  • Context 对象上的 Provider 作为提供者,Context 对象上的 Consumer 作为消费者

2、提供者:Provider

const ThemeProvider = ThemeContext.Provider  //提供者
export default function ProviderDemo(){const [ contextValue , setContextValue ] = React.useState({  color:'#ccc', background:'pink' })return <div><ThemeProvider value={ contextValue } > <Son /></ThemeProvider></div>
}

provider 作用:

  • value 属性传递 context,供给 Consumer 使用。
  • value 属性改变,ThemeProvider 会让消费 Provider value 的组件重新渲染。

Provider 特性总结:

  • Provider 作为提供者传递 context ,provider中value属性改变会使所有消费context的组件重新更新。

  • Provider可以逐层传递context,下一层Provider会覆盖上一层Provider。

3、消费者:Consumer

① 类组件之contextType 方式

const ThemeContext = React.createContext(null)
// 类组件 - contextType 方式
class ConsumerDemo extends React.Component{render(){const { color,background } = this.contextreturn <div style={{ color,background } } >消费者</div> }
}
ConsumerDemo.contextType = ThemeContextconst Son = ()=> <ConsumerDemo />
  • 类组件的静态属性上的 contextType 属性,指向需要获取的 context( demo 中的 ThemeContext ),就可以方便获取到最近一层 Provider 提供的 contextValue 值

② 函数组件之 useContext 方式

const ThemeContext = React.createContext(null)
// 函数组件 - useContext方式
function ConsumerDemo(){const  contextValue = React.useContext(ThemeContext) const { color,background } = contextValuereturn <div style={{ color,background } } >消费者</div>
}
const Son = ()=> <ConsumerDemo />
  • useContext 接受一个参数,就是 context 对象 ,返回一个 value 值,就是最近的 provider 提供 contextValue 值

③ 订阅者之 Consumer 方式

const ThemeConsumer = ThemeContext.Consumer // 订阅消费者function ConsumerDemo(props){const { color,background } = propsreturn <div style={{ color,background } } >消费者</div>
}
const Son = () => (<ThemeConsumer>{ /* 将 context 内容转化成 props  */ }{ (contextValue)=> <ConsumerDemo  {...contextValue}  /> }</ThemeConsumer>
)
  • Consumer 订阅者采取 render props 方式,接受最近一层 provider 中value 属性,作为 render props 函数的参数,可以将参数取出来,作为 props 混入 ConsumerDemo 组件,说白了就是 context 变成了 props。

总结:在 Provider 里 value 的改变,会使引用①contextType,②useContext 消费该 context 的组件重新 render ,同样会使 ③Consumer 的 children 函数重新执行,与前两种方式不同的是 Consumer 方式,当 context 内容改变的时候,不会让引用 Consumer 的父组件重新更新。

问题1:如何阻止 Provider value 改变造成的 children 不必要的渲染?

  • ① 第一种就是利用 memo,pureComponent 对子组件 props 进行浅比较处理。
const Son = React.memo(()=> <ConsumerDemo />)
  • ② 第二种就是 React 本身对 React element 对象的缓存。React 每次执行 render 都会调用 createElement 形成新的 React element 对象,如果把 React element 缓存下来,下一次调和更新时候,就会跳过该 React element 对应 fiber 的更新。
<ThemeProvider value={ contextValue } >{ React.useMemo(()=>  <Son /> ,[]) }
</ThemeProvider>

问题2:context 与 props 和 react-redux 的对比?

context 相比较 props 解决了:

  • 解决了 props 需要每一层都手动添加 props 的缺陷
  • 解决了改变 value ,组件全部重新渲染的缺陷

react-redux 就是通过 Provider 模式把 redux 中的 store 注入到组件中的

Context 原理

1、Context对象

通过 createContext 创建一个 context 。

  • Provider 本质上是一个 element 对象 $$typeof -> REACT_PROVIDER_TYPE
  • Consumer 本质上也是一个 element 对象 $$typeof -> REACT_CONTEXT_TYPE
  • _currentValue 这个用来保存传递给 Provider 的 value

2、Provider 提供者

Provider 本质上是一个特殊的 React Element 对象

jsx -> element -> fiber 关系:

  • <Provider> -> REACT_PROVIDER_TYPE React element -> ContextProvider fiber

在 fiber 调和阶段会进入到 beginWork 流程:

  • 如果当前类型的 fiber 不需要更新,那么会 FinishedWork 中止当前节点和子节点的更新。
  • 如果当前类型 fiber 需要更新,那么会调用不同类型 fiber 的处理方法。updateContextProvider 方法

1、updateContextProvider

  • 调用 pushProvider,会获取 type 属性上的 _context 对象,将 Provider 的 value 属性,赋值给 context 的 _currentValue 属性上。

  • 通过 calculateChangedBits 计算出 changedBits,当它返回的 changedBits === 0 的时候,那么还会浅比较 children 是否发生变化和有没有 legacy context,不需要更新,那么会 return 停止向下调和子节点

  • 否则,context改变,调用propagateContextChange进行更新

问:Proider 通过什么手段传递 context value?

答:即通过挂载 context 的 _currentValue 属性


2、propagateContextChange

  • 深度遍历所有的子代 fiber ,然后找到里面具有 dependencies 的属性。

  • 对比 dependencies 中的 context 和当前 Provider 的 context 是否是同一个,如果是,如果当前 fiber 是类组件,绑定一个 forceUpdate 标识 。

  • 然后会提高 fiber 的更新优先级,让 fiber 在接下来的调和过程中,处于一个高优先级待更新的状态。

  • 找到当前 fiber 向上的父级链上的 fiber ,统一更新他们的优先级,使之变成高优先级待更新状态。

  • 对高优先级的fiber进行 beginWork

dependencies 属性,这个属性可以把当前的 fiber 和 context 建立起关联,是一个链表结构,将 fiber 对应的 context 存放在 dependencies 中。

注意:一个 fiber 可以有多个 context 与之对应。

问题1、什么情况下 fiber 存在 dependencies—— 即 什么情况下使用 context

  • contextType 静态属性指向的类组件
  • 使用 useContext hooks 的函数组件
  • context 提供的 Consumer

问题2、 为什么class 组件会创建一个 ForceUpdate 的 update

context 要突破这些控制,就要做到当 value 改变,消费 context 的类组件更新,则需要通过 forceUpdate 强制更新。通过 PureComponentshouldComponentUpdate 等层层阻碍,解决了类组件更新限制。


3、beginWork 更新流程

React 会从 rootFiber 开始更新,每一个更新 fiber 都会走 beginWork 流程,开始找不同,找到有没有需要更新的地方,其中一个重要的寻找指标就是 更新的优先级

父子节点中形成了一条 root fiber -> 父 fiber -> 子 fiber 的 beginWork

  • 第一种: 如果遇到组件,而且 更新不涉及当前组件,也不在当前组件的父子递归链上,那么就不会 render,也不会向下 beginWork 。
  • 第二种: 如果遇到组件,而且 更新不涉及当前组件,但是更新组件属于当前组件的子孙后代,那么不会 render,但是会向下 beginWork ,目的很明确,找到对应的更新组件。
  • 第三种: 如果遇到 其他类型的 fiber 比如 hostComponent <div> ,那么会对比当前的更新优先级,如果低优先级,那么不需要向下 beginWork 。反之向下 beginWork。

总结流程如下:

  • 如果一个组件发生更新,那么当前组件到 fiber root 上的父级链上的所有 fiber ,更新优先级都会升高,都会触发 beginwork 。
  • render 不等于 beginWork,但是 render 发生,一定触发了 beginwork
  • 一次 beginwork ,一个 fiber 下的同级兄弟 fiber 会发生对比,找到任务优先级高的 fiber 。向下 beginwork 。

Provider 原理Provider 更新,会递归所有的子组件,只要消费了 context 的子代 fiber ,都会给一个高优先级。而且向上更新父级 fiber 链上的优先级,让所有父级 fiber 都处于一个高优先级。那么接下来高优先级的 fiber 都会 beginWork 。

多个 Provider 嵌套

如果有多个 Provider 的情况,那么后一个 contextValue 会覆盖前一个 contextValue,在开发者脑海中,要有一个定律就是:Provider 是用来传递 value,而非保存 value 。

3、Consumer 消费者

Consumer 本质上是类型为 REACT_CONTEXT_TYPE 的 element 对象。在调和阶段,会转化成 ContextConsumer 类型的 fiber 对象。

在 beginwork 中,会调用 updateContextConsumer 方法。

1、updateContextConsumer

  • 首先调用 readContext 获取最新的 value
  • 然后通过 render props 函数,传入最新的 value,得到最新的 children
  • 接下来 调和 children

fiber 上的 dependencies 如何和 context 建立起关联。

2、readContext

  • 首先会创建一个 contextItem
  • fiber 上会存在多个 dependencies ,它们以链表的形式联系到一起,如果不存在最后一个 context dependency , context dependencies 为空 ,那么会创建第一个 dependency 。
  • 如果存在 最后一个dependency,那么 contextItem 会以链表形式保存,并变成最后一个 lastContextDependency 。

useContext 和 contextType

1、useContext 原理,调用 useContext 本质上调用 readContext 方法。

  • 函数组件通过 readContext ,将函数组件的 dependencies和当前 context 建立起关联,context 改变,将当前函数组件设置高优先级,促使其渲染。

2、类组件的静态属性 contextType,本质上就是调用 readContext 方法。

  • 静态属性 contextType ,在类组件实例化的时候被使用,本质上也是调用 readContext将 context 和 fiber 上的 dependencies 建立起关联。

Context 原理

  • Provider 传递流程:Provider 的更新,会 深度遍历子代 fiber,消费 context 的 fiber 和父级链都会 提升更新优先级。 对于类组件的 fiber ,会 forceUpdate 处理。接下来所有消费的 fiber,都会 beginWork
  • Context 订阅流程contextTypeuseContextConsumer 会内部调用 readContext ,readContext 会把 fiber 上的 dependencies 属性context 对象 建立起关联。

总结

1、Context的用法

2、Context的原理

React Context 原理理解相关推荐

  1. 使用react的好处_聊一聊我对 React Context 的理解以及应用

    前言 Context被翻译为上下文,在编程领域,这是一个经常会接触到的概念,React中也有. 在React的官方文档中,Context被归类为高级部分(Advanced),属于React的高级API ...

  2. React Hooks 原理理解

    文章目录 Hooks hooks与fiber(workInProgress) 状态派发--useState(useReducer)原理 处理副作用--useEffect(useLayoutEffect ...

  3. react源码分析:深度理解React.Context

    开篇 在 React 中提供了一种「数据管理」机制:React.context,大家可能对它比较陌生,日常开发直接使用它的场景也并不多. 但提起 react-redux 通过 Provider 将 s ...

  4. [react] 说说你对reader的context的理解

    [react] 说说你对reader的context的理解 这个题想问的应该是render函数的上下文,而非React.createContext的那个Context. 不管是class组件还是函数式 ...

  5. react如何遍历并比较_[前端进阶] 这可能是最通俗易懂的React 渲染原理及性能优化...

    如今的前端,框架横行,出去面试问到框架是常有的事. 我比较常用React, 这里就写了一篇 React 基础原理的内容, 面试基本上也就问这些, 分享给大家. React 是什么 React是一个专注 ...

  6. 从源码角度了解react工作原理

    为什么要用虚拟dom DOM操作很慢,轻微的操作都可能导致⻚面重新排版,非常耗性能.相对于DOM对象(dom对象打印出来很大,很难diff),js对象 处理起来更快,而且更简单.通过diff算法对比新 ...

  7. [react] 简要描述下你知道的react工作原理是什么?

    [react] 简要描述下你知道的react工作原理是什么? 我理解的核心部分: 通过虚拟DOM表达真实DOM 通过数据驱动更新虚拟DOM进而更新真实DOM(MVVM) 有一套完整并且合理的 DOM ...

  8. gatsby_与Gatsby一起使用React Context API

    gatsby I'm a bit late to the party using the new React Context API, I did get to use it the other da ...

  9. RN:React Native原理以及新架构JSI、Fabric等概念

    说明 RN需要一个JS的运行环境, 在IOS上直接使用内置的javascriptcore, 在Android 则使用webkit.org官方开源的jsc.so. 此外还集成了其他开源组件,如fresc ...

最新文章

  1. 一篇简单易懂的原理文章,让你把JVM玩弄与手掌之中
  2. 动态参数与静态参数的判断、修改
  3. C语言文本操作以及C语言小技巧
  4. 【MFC】对话框中创建工具栏
  5. Nginx的安全控制及SSL加密介绍
  6. python isupper需要调包吗_密码强度等级
  7. 【干货】信息技术应用创新产业深度研究:204页报告深度解析信创产业全景图.pdf(附下载链接)...
  8. Springboot项目搭建(前端到数据库,超详细,附详细步骤截图)
  9. 【呆萌の研究】JavaScript常见的继承方式
  10. 深度神经网络基本问题的原理详细分析和推导
  11. php ezsql,ezSQL PHP数据库操作类库
  12. Android 学生管理系统
  13. 怎么知道网站是否被黑 服务器是否被入侵呢
  14. 网易考拉API,根据ID取产品详情 OneBound数据
  15. uni-app的介绍
  16. 渣土车管理实施方案,运输监控管理系统介绍
  17. 历数金融危机 摘自http://www.ftchinese.com/sc/index.jsp
  18. 达尔文的进化论正确吗?
  19. MFC实现像素鸟功能和一些改进
  20. Python pandas库|任凭弱水三千,我只取一瓢饮(3)

热门文章

  1. oracle 行转列的sql,Oracle行转列、列转行的Sql语句总结
  2. html新闻排版制作代码,在DIV+CSS排版中新闻列表的制作方法_CSS/HTML
  3. Java中的char究竟能存中文吗?
  4. ie11 no java plugin,修復Javascript在IE11中不起作用
  5. 搞个这样的APP要多久?
  6. 【Python爬虫】利用爬虫抓取双色球开奖号码,获取完整数据,简洁45行代码实现,更新时间2023-06-28
  7. 2019年第三期全国高校大数据与人工智能骨干师资研修班
  8. 标记用来标识一个html文件中的表格,大学生计算机网页制作考试模拟题6
  9. WPS JS宏批量重命名文件名
  10. 如何解析json并展示到html页面中,js解析json并生成html页面