• 对 React Context 的理解以及应用


    在React的官方文档中,Context被归类为高级部分(Advanced),属于React的高级API,但官方并不建议在稳定版的App中使用Context。

    很多优秀的React组件都通过Context来完成自己的功能:

    • 比如react-redux的,就是通过Context提供一个全局态的store;
    • 拖拽组件react-dnd,通过Context在组件中分发DOM的Drag和Drop事件;
    • 路由组件react-router通过Context管理路由状态等等。

    在React组件开发中,如果用好Context,可以让你的组件变得强大,而且灵活。

    简单说就是,当你不想在组件树中通过逐层传递props或者state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递。

    使用props或者state传递数据,数据自顶下流。

    适用于所有16.x版本(旧版本之后会被废弃)

    如何使用Context

    如果要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,或者一些缓存数据,这比替代方案要简单的多。

    API

    React.createContext

    const MyContext = React.createContext(defaultValue);
    

    创建一个Context对象。当React渲染一个订阅了这个Context对象的组件,这个组件会从组件树中离自身最近的那个匹配的Provider中读取到当前的上下文值。

    Context.Provider

    <MyContext.Provider value={/* 某个值 */}>
    

    每个Context对象都会返回一个Provider React组件,它允许消费组件订阅context的变化。

    当提供者的value值发生变化时,它内部的所有消费组件都会重新渲染。

    提供者及其内部消费者组件都不受制shouldComponentUpdate函数,因此当消费者组件在其祖先组件退出更新的情况下也能更新。

    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

  • 相关阅读:
    基础架构:一条SQL查询语句是如何执行的?
    常用的字符串转换日期格式
    JSONObject.fromObject(map)(JSON与JAVA数据的转换)
    ModelAndView的介绍
    Springmvc常用注解
    @PathVariable注解
    @ModelAttribute运用详解
    struts2将servlet对象注入到Action中
    为什么要继承ActionSupport?
    mysql中like用法
  • 原文地址:https://www.cnblogs.com/adoctors/p/11046866.html
Copyright © 2020-2023  润新知