React Context(上下文)

React Context(上下文)

一.初识Context

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法.

二.作用

Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言

context可以进行跨级组件通信.其原理是生产-消费者模式

三.使用Context

1.React.createContext

创建一个上下文容器组件

这个组件会从组件树中离自身最近的那个匹配的Provider中,读取到当前的context值

//定义一个全局的context
const GlobalContext = React.createContext(defaultValue);

PS:defaultValue是默认的数据,当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效,将 undefined 传递给 Provider 的 value 时,消费组件的 defaultValue 不会生效。

2.Provider

生产者,用于生产共享数据的地方, 在value中放置需要共享的数据

provider接收value属性,传递给消费者,一个provider对应多个consumer消费者,当provider的value值发生变化,所有的消费者组件,都会重新渲染.

class Son extends React.Component {
    render() {
        return (
            //提供一个生产者
            <GlobalContext.Provider value={
                {
                    //传递给消费者的数据
                    msg: this.state.msg,
                    //传递给消费者,一个函数,提供改变生产者内部的一个参数
                    onChangeShow: (text) => {
                        this.setState({
                            msg: text
                        });
                    },
                }
            }>
                <div>
                    <h1>我是父组件</h1>
                </div>
            </GlobalContext.Provider>
        );
}

3.Consumer

消费者,专门消费生产者提供的数据

接收当前的context值,返回一个React节点,获取到的value值是往上组件树离这个context最近的生产者提供的value,如果没有生产者,会直接读取createContext()对象的默认值defaultValue

class Son extends React.Component {
    render() {
        return (
            <GlobalContext.Consumer>
                {
                    context => (
                        <div>
                            <h1>我是儿子组件,我要消费父亲的数据</h1>
                        </div>
                    )
                }
            </GlobalContext.Consumer>
        );
    }
}

4.Class.contextType

需要消费生产者提供的数据,还有一种方法,也是可以的,设置组件的属性为contextType,值为React.createContext()创建的上下文context对象,可以直接通过this.context来访问最近的Context上的值,任何生命周期都可以访问

class Hello extends React.Component {
    render() {
        console.log(this.context);
        return (
            <div>

            </div>
        );
    }
}
Hello.contextType = GlobalContext;

5.Context.displayName

可以通过给当前的上下文对象设置一个名字,便于在DevToolszhong中查看调试

const GlobalContext = React.createContext();
GlobalContext.displayName = 'MyDisplayName';

<GlobalContext.Provider> // "MyDisplayName.Provider" 在 DevTools 中
<GlobalContext.Consumer> // "MyDisplayName.Consumer" 在 DevTools 中

6.注意事项

因为 context 会使用参考标识(reference identity)来决定何时进行渲染,这里可能会有一些陷阱,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例子,当每一次 Provider 重渲染时,以下的代码会重渲染所有下面的 consumers 组件,因为 value 属性总是被赋值为新的对象:

class App extends React.Component {
  render() {
    return (
      <MyContext.Provider value={{something: 'something'}}>
        <Toolbar />
      </MyContext.Provider>
    );
  }
}

为了防止这种情况,将 value 状态提升到父节点的 state 里:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: {something: 'something'},    };
  }

  render() {
    return (
      <Provider value={this.state.value}>        <Toolbar />
      </Provider>
    );
  }
}

所以,在Provider中定义的属性value,最好是在state先声明,之后再赋值给当前的value属性

四,应用场景

1.主题切换

export const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};

export const ThemeContext = React.createContext(
  themes.dark // 默认值
);

2.获取当前认证的用户

3.React-router使用context

4.React-redux使用contxt

5.跨层级组件传参

context使用场景还有很多,不一一列举了!

五.缺点

context主要场景在于很多不同层级的组件需要访问同样的一些数据,但是不建议使用,因为当结构复杂时,这种全局变量不易追溯到源头,不知道从哪里传递过来的,会导致应用混乱,难以维护,更增强了程序的复杂性

如果只是为了避免层层传递属性,可以使用组件组合,可能效果会是比context更好的解决方案

猜你喜欢

转载自blog.csdn.net/liuqiao0327/article/details/107298277
今日推荐