对 React Context 的理解以及应用
在React的官方文档中,Context被归类为高级部分(Advanced),属于React的高级API,但官方并不建议在稳定版的App中使用Context。 很多优秀的React组件都通过Context来完成自己的功能:
在React组件开发中,如果用好Context,可以让你的组件变得强大,而且灵活。 简单说就是,当你不想在组件树中通过逐层传递props或者state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递。 使用props或者state传递数据,数据自顶下流。 适用于所有16.x版本(旧版本之后会被废弃)
如果要Context发挥作用,需要用到两种组件,一个是Context生产者(Provider),通常是一个父节点,另外是一个Context的消费者(Consumer),通常是一个或者多个子节点。所以Context的使用基于生产者消费者模式。 对于父组件,也就是Context生产者,需要通过一个静态属性childContextTypes声明提供给子组件的Context对象的属性,并实现一个实例getChildContext方法,返回一个代表Context的纯对象 (plain object) 。 import React from 'react' import PropTypes from 'prop-types' class ParentComponent extends React.Component { state = { parentVal:'parentVal' } // 声明Context对象属性 static childContextTypes = { name: PropTypes.string,fun: PropTypes.func } // 返回Context对象,方法名是约定好的 getChildContext () { return { name: 'parent-msg',fun: () => { const {parentVal}=this.state; console.log( 'from top msg','parent-state-parentVal:',parentVal ) } } } render () { return ( <div> <p>我是父级或者祖父</p> </div> ) } } 而对于Context的消费者,通过如下方式访问父组件提供的Context。 import React from 'react' import PropTypes from 'prop-types' class ChildComponent extends React.Component { // 声明需要使用的Context属性 static contextTypes = { name: PropTypes.string,fun: PropTypes.func } render () { const {name,fun} = this.context console.log(name) fun() } } 子组件需要通过一个静态属性contextTypes声明后,才能访问父组件Context对象的属性,否则,即使属性名没写错,拿到的对象也是undefined。 新版请谨慎使用,因为这会使得组件的复用性变差。 使用context的通用的场景包括管理当前的locale,theme,或者一些缓存数据,这比替代方案要简单的多。 APIReact.createContext const MyContext = React.createContext(defaultValue); 创建一个Context对象。当React渲染一个订阅了这个Context对象的组件,这个组件会从组件树中 Context.Provider <MyContext.Provider value={/* 某个值 */}> 每个Context对象都会返回一个Provider React组件,它允许消费组件订阅context的变化。 当提供者的value值发生变化时,它内部的所有消费组件都会重新渲染。 提供者及其内部消费者组件都 Class.contextType class MyClass extends React.Component { componentDidMount() { let value = this.context; /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */ } componentDidUpdate() { let value = this.context; /* ... */ } componentWillUnmount() { let value = this.context; /* ... */ } render() { let value = this.context; /* 基于 MyContext 组件的值进行渲染 */ } } MyClass.contextType = MyContext; 挂载在类上的contextType属性会被重赋值为一个由React.createContext()创建的Context对象。 这能让你使用this.context来消费最近Context上的那个值。你可以在任何生命周期中访问到它,包括渲染函数中。 可以使用static这个类属性来初始化你的contextType。 class MyClass extends React.Component { static contextType = MyContext; render() { let value = this.context; /* 基于这个值进行渲染工作 */ } } Context.Consumer <MyContext.Consumer> {value => /* 基于 context 值进行渲染*/} </MyContext.Consumer> 传递给函数的value值等同于往上组件树离这个context最近的提供者提供的value值。如果没有对应的Provider,value参数等同于传递给createContext()的defaultValue。 注意:当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。 示例关键语句 ( 类似与 eventBus 里的 bus.js 作为一个媒介,该句用于方便理解 ) createContext.js import React from 'react'; export default React.createContext('createContext-test-defaultVal'); 提供初始 context 值的组件 parent.js import React from 'react'; import createContext from './createContext'; // 引入媒介 render() { // 在 createContext 内部的 ComTest 组件使用 state 中的 name 值, // 而外部的组件使用默认的值 return ( <div> <createContext.Provider value={this.state.name}> // 内部可以获取到分发的值 <ComTest fun={this.fun} /> <Tier0 /> </createContext.Provider> <div> <ComTest /> </div> </div> ); } 要获取context值的组件 只要在 createContext.Provider 里,不用管中间隔了多少层,只需要在使用context的组件内引入媒介即可 import React from 'react'; import createContext from './createContext'; // 引入媒介 class ComTest extends React.Component { static contextType = createContext; componentDidMount(){ console.log(this.context) } render(){ return ( <div> <p>此处为获取数据的子组件 : {this.context}</p> </div> ) } } // ComTest.contextType = createContext; export default ComTest; 多个 Context为了确保 context 快速进行重渲染,React 需要使每一个 consumers 组件的 context 在组件树中成为一个单独的节点。 // Theme context,默认的 theme 是 “light” 值 const ThemeContext = React.createContext('light'); // 用户登录 context const UserContext = React.createContext({name: 'Guest',}); class App extends React.Component { render() { const {signedInUser,theme} = this.props; // 提供初始 context 值的 App 组件 return ( <ThemeContext.Provider value={theme}> <UserContext.Provider value={signedInUser}> <Layout /> </UserContext.Provider> </ThemeContext.Provider> ); } } function Layout() { return ( <div> <Sidebar /> <Content /> </div> ); } // 一个组件可能会消费多个 context function Content() { return ( <ThemeContext.Consumer> {theme => ( // 对应 ThemeContext.Provider 的 value <UserContext.Consumer> {user => ( // 对应 UserContext.Provider 的 value <Child user={user} theme={theme} /> )} </UserContext.Consumer> )} </ThemeContext.Consumer> ); } 官方文档:https://zh-hans.reactjs.org/docs/context.html#___gatsby (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |