美文网首页
react Context API

react Context API

作者: sweetBoy_9126 | 来源:发表于2019-09-27 10:37 被阅读0次

一个简单的四层函数参数传递的demo

  1. 原生js写法
function f1(n1) {
  console.log(n1);
  f2(n1);
}
function f2(n2) {
  console.log(n2);
  f3(n2);
}
function f3(n3) {
  console.log(n3);
  f4(n3);
}
function f4(n4) {
  console.log(n4);
}
function f(n) {
  f1(n);
}
{
  let n = 100;
  f(n);
}

上面的代码我如果要在每个函数里拿到n就需要一直把n作为参数传递下去

  1. react写法
function F1(props) {
  return (
    <div>
      {props.n1}
      <F2 n2={props.n1} />
    </div>
  );
}
function F2(props) {
  return (
    <div>
      {props.n2}
      <F3 n3={props.n2} />
    </div>
  );
}
function F3(props) {
  return (
    <div>
      {props.n3}
      <F4 n4={props.n3} />
    </div>
  );
}
function F4(props) {
  return <div>{props.n4}</div>;
}
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 100
    };
  }
  render() {
    return (
      <div>
        aaa
        <F1 n1={this.state.n} />
      </div>
    );
  }
}
ReactDOM.render(<App />, document.querySelector("#root"));

现在我们如果想在F4里获取到state里的n,我们也必须得一层一层通过props传递下去,也就是说即使我们不需要在F2和F3中获取n我们也得传下去,这样的代码写起来就很冗余很复杂

代码改进

  1. 对原生js代码改进
    (1). 把n作为全局变量,这样f4就可以直接访问到n了
let n = 100
function f4() {
  console.log(n) // 100
}

问题:全局变量有可能会被人随意的修改,所以我们要慎用全局变量
(2). 使用局部全局变量

{
  let context = {};
  window.setContext = (key, value) => {
    context[key] = value;
  };
  window.f1 = () => {
    f2();
  };
  function f2() {
    f3();
  }
  function f3() {
    f4();
  }
  function f4() {
    console.log(context.n);
  }
}
window.setContext("n", 100);
window.f1();

上面的代码我们的context是一个局部变量,我们外界获取不到它,而修改它的唯一方式是通过一个全局的setContext方法修改,因为我们的f4和context是在同一个作用域所以可以直接获取到我们的context里面的值

  1. 对react代码进行改进
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
function F1(props) {
    return (
        <F2 />
    )
}
function F2(props) {
    return (
        <F3 />
    )
}
function F3(props) {
    return (
        <div>
            <nContext.Consumer>
                {(n) => <F4 n4={n} />}
            </nContext.Consumer>
        </div>
    )
}
function F4(props) {
    return (
        <div>{props.n4}</div>
    )
}
const nContext = React.createContext()
class App extends React.Component {
    render() {
        return (
            <div>
                <nContext.Provider value="999">
                    <F1 />
                </nContext.Provider>
            </div>
        )
    }
}

ReactDOM.render(<App />, document.getElementById('root'));

声明一个React.createContext变量,然后通过它的Provider指定一个value为初始值,需要获取值的地方通过它的Consumer,然后标签里面是一个函数返回你引用的组件,然后通过函数里的参数可以拿到value的值,之后在对应组件中还是通过props获取

自己写一个接受函数的组件,以便理解<nContext.Consumer>

function Consumer(props) {
    // 这里打印出来的是F1函数
    console.log(props.children)
    return (
        <div>{props.c1}</div>
    )
}
function F1() {
    return 'F1'
}
class App extends React.Component {
    render() {
        return (
            <div>
                <Consumer c1="c1">
                    {F1}
                </Consumer>
            </div>
        )
    }
}
  1. 我们可以ton过props.children拿到Consumer标签里的内容也就是{F1},所以我们可以直接在Consumer函数里调用F1
function Consumer(props) {
    // 调用标签里的函数
    props.children()
    return (
        <div>{props.c1}</div>
    )
}
<Consumer c1="c1">
   {F1}
</Consumer>
  1. 因为我们的F1实际上就是一个函数声明,所以我们可以直接写成函数声明
<Consumer c1="c1">
   {() => console.log('我被调用了')}
</Consumer>
  1. 在我们的箭头函数声明里面传入一个参数
<Consumer>
   {(n) => console.log('我被调用了', n)}
</Consumer>

我们就需要在调用的地方传入一个实参

function Consumer(props) {
    // 调用标签里的函数
    let x = 100
    props.children(x)
    return (
        <div>{props.children}</div>
    )
}

所以我们的n就可以拿到100

  1. 变成{(n) => <F4 n4={n} />}的形式
function Consumer(props) {
    let x = 100
    let result = props.children(x)
    return (
        <div>{result}</div>
    )
}
function F1() {
    return 'F1'
}
class App extends React.Component {
    render() {
        return (
            <div>
                <Consumer>
                    {(n) => <div>{n}</div>}
                </Consumer>
            </div>
        )
    }
}

上面的props.children(x)返回的是<div>{n}</div>,所以Consumer的返回值也就是<div><div>{n}</div></div>{n}是100,所以就等价于

function Consumer(props) {
    return (
        <div>
          <div>100</div>
        </div>
    )
}

更改context里的value值

  1. 组件本身改变
class App extends React.Component {
    constructor() {
        super()
        this.state = {
            n: 100
        }
        setTimeout(() => {
            this.setState({
                n: this.state.n + 10
            })
        }, 2000)
    }
    render() {
        return (
            <div>
                <nContext.Provider value={this.state.n}>
                    <F1 />
                </nContext.Provider>
            </div>
        )
    }
}
  1. 在其他组件中改变context的value
const nContext = React.createContext()
function F1() {
    return (
        <F2 />
    )
}
function F2() {
    return (
        <F3 />
    )
}
function F3() {
    return (
        <div>
            <nContext.Consumer>
                {x => <F4 n={x.n} setN={x.setN}/>}
            </nContext.Consumer>
        </div>
    )
}
function F4(props) {
    return (
        <div>
            {props.n}
            <button onClick={props.setN}>点我</button>
        </div>
    )
}
class App extends React.Component {
    constructor() {
        super()
        this.state = {
            x: {
                n: 300,
                setN: ()=> {
                    console.log('aaaa')
                    this.setState({
                        x: {
                            n: Math.random()
                        }
                    })
                }
            }
        }
    }
    render() {
        return (
            <div>
                <nContext.Provider value={this.state.x}>
                    <F1 />
                </nContext.Provider>
            </div>
        )
    }
}

ReactDOM.render(<App />, document.getElementById('root'));

问题:我们只有第一次点击按钮的时候value才会修改,之后再次点击value就不会修改了

补充知识:修改state里一个对象的属性,要把整个对象都重新写一遍,然后这个对象里面还要把它之前的属性都扩展到新的对象里,而不能直接对象.对应的属性
比如修改下面的x里面的n,我们就得把整个x对象都重新赋值一遍,然后对象里面还要写...this.state.x

错误写法:直接修改'x.n'

this.state = {
  x: {
    n: 300,
    setN: () => {
      'x.n': Math.random()
    }
  }
}

正确写法:

this.state = {
    x: {
        n: 300,
        setN: ()=> {
            this.setState({
                x: {
                     ...this.state.x,
                    n: Math.random()
                }
            })
        }
    }
}

相关文章

网友评论

      本文标题:react Context API

      本文链接:https://www.haomeiwen.com/subject/yxqmuctx.html