【React】浅谈Context的使用

老样子我们边写小demo边理解context。
组件继承顺序:
index.js–>home.js–>show.js–>b.js(c.js)

在context中,Provider和Consumer有两种定义的方式:
1、const { Provider, Consumer } = React.createContext(defaultValue);
2、const testContext = React.createContext(defaultValue);
<testContext.Provider /> <testContext.Consumer />
首先,测试下通过祖先组件–>父组件–>子组件的传递:
在home.js中,我们定义一个Provider和即将传递的value:

import React, { Component } from 'react';
import Show from './show'


export const { Provider, Consumer } = React.createContext('hhhhh');
class Home extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    let value = 'hekki'
    return (
      <Provider value={value}>
        <div>预备传的值:{value}</div>
        <Show />
      </Provider>
    )

  }
}

export default Home;

createContext(defaultValue):defaultValue选填
export const { Provider, Consumer }:抛出Provider和Consumer,子组件将通过Consumer接收传递的value

在show.js中订阅value:

import React, { Component } from 'react';
import B from './b';
import C from './c';
import { Consumer } from './home';
import { withRouter } from 'react-router-dom';

class Show extends Component {

  render() {

    return (

      <div>
        <Consumer>
          {(value) =>
            <div>
              <div>子组件的传值:{value}</div>
              <B />
              <C />
            </div>
          }

        </Consumer>
      </div>

    )
  }
}

export default withRouter(Show);

value的获取通过回调函数获取,同时也可以再次向下传递值
在b.js订阅value:

import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { Consumer } from './home'

class B extends Component {
  render() {
    return (
      <Consumer>
        {(value) =>
          <div >
            我是B组件,接收的值是:{value}
          </div>
        }

      </Consumer>
    )
  }
}
export default withRouter(B);

最终的效果如下:
在这里插入图片描述
现在来测试一下祖先组件–>子组件的情况:
我们将父组件的订阅内容清空:

  return (
      <div>
        <B />
        <C />
      </div>
    )

再看实现的效果:
在这里插入图片描述
那么,我们定义多个Provider呢?
未提及的代码不做更改,我在show.js中又定义了一个Provider:

export const { Provider, Consumer } = React.createContext();
class Show extends Component {

  render() {
    const show = '我是第二个context定义的全局的值'
    return (

      <div>
        <Provider value={show}>
          <B />
          <C />
        </Provider>
      </div>

    )
  }
}

再到c.js做出更改:

import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { Consumer } from './show'


class C extends Component {
  render() {
    return (
      <div >
        <Consumer >
          {(value) =>
            <div>我是C组件,接收的值是:{value}</div>
          }

        </Consumer>
      </div>

    )
  }
}
export default withRouter(C);

最终的效果如下:
在这里插入图片描述
如果,非该组件的子组件Provider之后能够订阅到值吗?
我再定义了一个a.js组件,这个组件没有存在在上面的继承链上,写好Provider之后再由b.js引用:
a.js:

export const testContext = React.createContext();
class A extends Component {
  render() {
    return (
      <testContext.Provider value={'helloworld'}>
        <div>我是A组件</div>
      </testContext.Provider>
    )
  }
} 

b.js:

class B extends Component {
  render() {
    return (
      <div>
        <Consumer>
          {(value) =>
            <div >
              我是B组件,接收的值是:{value}
            </div>
          }
        </Consumer>

        <testContext.Consumer>
          {
            (value) =>
              <div>这里显示接收的A组件的值:{value}</div>

          }
        </testContext.Consumer>
      </div>
    )
  }
}

在这里插入图片描述
可以得出结论是:Provider里面一定要包含继承的子组件,在继承链上是可以跳跃引用的(子组件引用祖先组件),如果不包含继承的子组件,则不能被Consumer引用。在一个工程中,可以有多个context存在。
在上述情况下,溯源是比较好找的,因为export一个Provider,对应一个Consumer。在需要接收内容的地方,会引用对应的Consumer。
那么,如果需要传值动态更改要怎么办呢?
为了方便观察,我将b.js屏蔽,修改show.js的代码:

export const { Provider, Consumer } = React.createContext();
class Show extends Component {
  constructor() {
    super();
    this.state = {
      isClick: false
    }
  }


  handleOnClick = () => {
    const isClick = !this.state.isClick;
    this.setState({
      isClick
    })
  }



  render() {
    return (
      <div>
        <Provider value={this.state}>
          <Button onClick={() => { this.handleOnClick() }} >点击改变数据</Button>
          {/* <B /> */}
          <C />
        </Provider>
      </div>

    )
  }
}

在这里,我将value定义成了父组件的state,再定义了一个按钮,当按下按钮的时候,改变state里的值,这样,子组件接收的值就会实时改变。
这是对应的子组件接收值的代码:

class C extends Component {
  render() {
    console.log(this.props)
    return (
      <div >
        <Consumer >
          {(value) =>
            <div>我是C组件,接收的值是:{value.isClick ? 'hello' : 'goodbye'}</div>
          }

        </Consumer>
      </div>

    )
  }
}

实现的效果如图:
在这里插入图片描述
context也支持嵌套,为了方便理解,我又一次修改show.js

export const { Provider, Consumer } = React.createContext();
export const fistText = React.createContext();
export const secondText = React.createContext();
class Show extends Component {
  constructor() {
    super();
    this.state = {
      isClick: false
    }
  }


  handleOnClick = () => {
    const isClick = !this.state.isClick;
    this.setState({
      isClick
    })
  }



  render() {
    return (

      <div>
        <Provider value={this.state}>
          <Button onClick={() => { this.handleOnClick() }} >点击改变数据</Button>
          <B />
          <C />
        </Provider>
        <fistText.Provider value={'这是第一个context的传值'}>
          <secondText.Provider value={'这是第二个context的传值'}>
            <D />
          </secondText.Provider>

        </fistText.Provider>

      </div>

    )
  }
}

对应的d.js:

import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { fistText, secondText } from './show'

export const testContext = React.createContext();
class D extends Component {
  render() {
    return (
      <fistText.Consumer>
        {(value) =>
          <secondText.Consumer>
            {(val) =>
              <div>
                我是D组件,我接收的值:{value},{val}
              </div>
            }
          </secondText.Consumer>
        }
      </fistText.Consumer>
    )
  }
}
export default withRouter(D);

得到的效果:
在这里插入图片描述
最后,对context做一个总结:
1、由React.createContext()注册,有两种写法(前文已讲),Provider定义传递数值,Consumer订阅
2、context是对继承链传值的一个优化,将需要继承的值提出来作为全局变量,如果子组件需要继承祖先组件的数据不需要一级一级的porps传值,可直接引用。
3、子组件不能更改context,但是父组件可以实时更改传递的值
4、context支持嵌套,但是太深的嵌套建议优化结构。
5、和Redux相比,Redux和Vuex功能类似,是将需要全局使用的变量提出来,任何组件可以修改他们,不用考虑继承,不用考虑相互间是否是子组件。但是Redux是单独的一套体系,如果工程、值传递不复杂的情况下,context可以提高优先级。

如果有不正确的,欢迎指正,我及时修改。
完整代码地址:https://github.com/PYLDora/React-Context
本文参考:
https://react.docschina.org/docs/context.html
https://www.jianshu.com/p/65b348bf86ad

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值