关于react-context的官方文档
https://reactjs.org/docs/context.htmlhtml
不少同窗认为一旦父子组件之间层级过深,多层级通信时候第一反应就是要用vuex和redux,但每每杀鸡用了宰牛刀。可是vue和react中提供了不少方法去达到数据共享的效果,而没必要显式地经过组件树的逐层传递 props。
好比在vue中provide 和 inject前端
一、这对选项须要一块儿使用,以容许一个祖先组件向其全部子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
二、provide提供数据,多层子组件 向上层寻找,只要找到 就不在向上层寻找了。
三、inject 向子组件注入数据;
vue中的mixin和extend也能够作到相关功能,这里就不作详细赘述,须要了解的同窗能够自行去观看。
下面回到本篇文章的正题react-context,React官网的高级指引中指出了Context,优雅的解决这个问题。vue
前端同窗都知道在js中的context指的是执行上下文,this指向谁,谁就是当前的执行上下文。react
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
意思即是context可以没必要显式地经过组件树的逐层传递 props,能够跨层级进行数据传递。vuex
先简单说下老版本,以前也是实验性的存在,我以为不太好用。生产者须要声明childContextTypes对象,对类数据类型进行校验,不提供就会报错。
具体的就不细说,须要了解的同窗自行查看。redux
学习context首先须要了解其三个核心的api: React.createContext()、Provider(发布者)、Consumer(订阅者)api
createContext()建立一个 Context 对象。浏览器
每一个 Context 对象都会返回一个 Provider React 组件,它容许消费组件订阅 context 的变化。ide
Provider 接收一个value
属性,传递给消费组件。一个 Provider 能够和多个消费组件有对应关系。多个 Provider 也能够嵌套使用,里层的会覆盖外层的数据。函数
当 Provider 的value
值发生变化时,它内部的全部消费组件都会从新渲染。Provider 及其内部 consumer 组件都不受制于shouldComponentUpdate
函数,所以当 consumer 组件在其祖先组件退出更新的状况下也能更新。
value的值天然是能够动态变动的,而且会传递给子组件中,点击变动value按钮会发现页面上的三个tomorrow变为三个yesterday。
import React, { useContext } from 'react' const ThemeContext = React.createContext('today') class App extends React.Component { state = { user: 'tomorrow' } change = () => { this.setState({ user: 'yesterday' }) } render() { const { user } = this.state // createContext默认值是“today”。 // 不管多深,任何组件都能读取这个值。 // 这里Provider 把value值 “tomorrow” 做为当前的值传递下去。 return ( <ThemeContext.Provider value={ user }> <div onClick={ this.change }>变动value</div> <Toolbar /> </ThemeContext.Provider> ); } }
这里是react-hook函数式写法,用到react-hook的API:useContext获取context的值
挂载在 class 上的contextType
属性会被重赋值为一个由React.createContext()
建立的 Context 对象。这能让你使用this.context
来消费最近 Context 上的那个值。你能够在任何生命周期中访问到它,包括 render 函数中
function Toolbar() { const context = useContext(ThemeContext) return ( <div> <div>{context}</div> <Head /> </div> ); }
经过ThemeContext的属性Consumer消费用户数据
Consumer的子组件必须是一个function,经过function的参数接收顶层传入的数据
任何订阅者(Consumer)均可以直接修改context,这会致使后续的订阅者获取到修改后的context值,但这显然是不可取的。
若是须要修改,应该统一由发布者(Provider)修改,也就是相似APP组件中变动value按钮
class Head extends React.Component { render() { return ( <ThemeContext.Consumer> { context => ( <div> <div>{this.context}</div> <Title /> </div> ) } </ThemeContext.Consumer> ) } }
最后的子组件显示context内容
context还有个Api:Context.displayName
想了解的同窗能够去官网看一下
class Title extends React.Component { static contextType = ThemeContext render() { return ( <div> {this.context} </div> ) } }
在 provider 的父组件进行重渲染时,可能会在 Consumers 组件中触发意外的渲染。
下面例子:对value直接赋值的时候就会触发这种没必要要的渲染
const ThemeContext = React.createContext(); class App extends React.Component { render() { return ( <ThemeContext.Provider value={ {name: '666'} }> <div onClick={ this.change }>变动value</div> <Toolbar /> </ThemeContext.Provider> ); } }
能够经过把value值放在state中解决这种问题
const ThemeContext = React.createContext(); class App extends React.Component { state = { name: '666' } change = () => { this.setState({ user: 'yesterday' }) } render() { const { user } = this.state return ( <ThemeContext.Provider value={ user }> <div onClick={ this.change }>变动value</div> <Toolbar /> </ThemeContext.Provider> ); } }
有没有发现redux的注入和context的注入很是类似。
经过React Developer Tools在浏览器很容易发现
react-redux其实是经过Provider组件和connect方法进行链接react和redux,那么他们到底本质上就是经过Context来传递数据
以上就是我的对react-context学习的一点总结。context虽然轻便,可是官方仍是没有大面积的去推广,仍是存在些问题,毕竟和强大的react-redux对比,没有很好的管理体系,普遍认知度也比较低。可是凡事没有绝对好坏,只有适不适合。