About React

210 阅读7分钟

1. React的诞生

1.1 JS加减法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <div>
        <span id="result">0</span>
        <button id="add">+</button>
        <button id="minus">-</button>
    </div>
</body>
<script>
    let result = document.querySelector('#result');
    let add = document.querySelector("#add");
    let minus = document.querySelector("#minus");

    add.addEventListener('click', function(){
        let number = parseInt(result.innerText, 10);
        numer += 1;
        result.innerText = number;
    })

    minus.addEventListener('click', function(){
        let number = parseInt(result.innerText, 10);
        numer -= 1;
        result.innerText = number;
    })
</script>
</html>

1.2 简化思维

  • 操作完填到页面,不从页面取,不去访问页面

1.3 代码版本进化

  • 初步版本
<body>
    <div id="root"></div>
    <button id="add"></button>
</body>


let number = 0;
render();
    
let add = document.querySelector("#add")
add.onclick = () => {
    number += 1;
    render();
}
// js中创建,操作,最后渲染页面
function render(){
    let span = React.createElement('span', {className: 'red'}, number);
    ReactDOM.render(span, document.querySelector('#root'));
}

1.3.1 改进版本1

<body>
    <div id="root"></div>
</body>
<script>
    let number = 0;

    let onClickButton = () =>{
        number += 1;
        render();
    }
    let onClickButton2 = () =>{
        number -= 1;
        render();
    }

    render();

    function render(){
        let span = React.createElement('span', {className: 'red'}, number);
        let button = React.createElement('button', {onClick: onClickButton}, '+');
        let button2 = React.createElement('button2', {onClick: onClickButton2}, '-');
        let div = React.createElement('div', {className: 'parent'}, span, button, button2);
        ReactDOM.render(div, document.querySelector('#root'));
    }
</script>

1.3.2 改进版本2

<body>
    <div id="root"></div>
</body>

<script>
    let h = React.createElement
    let number = 0;

    let onClickButton = () =>{
        number += 1;
        render();
    }
    let onClickButton2 = () =>{
        number -= 1;
        render();
    }

    render();

    function render(){
        let div = h('div', {className: 'parent'}, 
            h('span', {className: 'red'}, number), 
            h('button', {onClick: onClickButton}, '+'), 
            h('button2', {onClick: onClickButton2}, '-')
        );
        ReactDOM.render(div, document.querySelector('#root'));
    }
</script>
  • babel翻译

onClick={add()} 加括号写法,函数返回值赋值给onClick,错误

1.4 JSX版本

jsbin.com/piwazag/1/e…

let number = 0

let add = ()=>{
  number += 1
  render()
}

let minus = ()=>{
  number -= 1
  render()
}

render()

function render(){
  ReactDOM.render(
    <div>
      <span>{number}</span>
      <button onClick={add}>+</button>
      <button onClick={minus}>-</button>
    </div>,
    document.querySelector('#root')
  )
}
  • 虚拟DOM:表示DOM节点的对象,非真实的DOM,如上代码

2. 函数组件和Class组件

2.1 参数传递

function render(){
  ReactDOM.render(
    <App/>,  // 即 React.createElement(App) 语法糖
    document.querySelector('#root')
  )
}

2.1.1 函数组件参数传递

标签的属性就是函数的参数

function App(){
  return (
    <div>
      <Box1 name="booooooox1"/>   // 标签的属性就是函数的参数
      <Box2 name="booooooox2"/>
    </div>
  )
}

function Box1(obj){
    return(
    <div>{obj.name}</div>    // 不要修改传来的属性,有bug,只能读取
    )
}

函数组件没有自己的状态, Class组件有

2.2.2 Class组件参数传递

class App2 extends React.Component{
    render() {   //局部render
        <div>
            {this.props.name}
            {this.props.age}
        </div>
    }
}

function render() {
    ReactDOM.render(
    <App2/ name="paul" age="10">, 
    document.querySelector('#root')
  )
}

2.2.3 进化过程

// function组件传递参数过程

function App() {
    return(
        <div> app
            <Foo message = "你好"/>
        </div>
    )
}
function Foo(props) {
    return(
        <div> 得到的message{props.message}</div>
    )
}
ReactDOM.render(<App/>, document.querySelector("#app"))

/**
    此时Foo组件想回传一个click事件给App组件,App组件用function就不适用了
    子组件Foo想通知App组件改变Message
**/

Class App extends React.Component {
    constructor() {
        super()
        this.state = {
            message: '你好'
        }
    }
    changeMessage() {
        this.setState({ message: 'hello'})
    }
    render() {
        return(
          <div> app
            <Foo message = {this.state.message} fn = {this.changeMessage.bind(this)} />
          </div>
        )
    }
}

function Foo(props) {
    return(
        <div> 得到的message{props.message}
            <button onClick = {props.fn} >Click</button>
        </div>
    )
}
ReactDOM.render(<App/>, document.querySelector("#app"))

2.2 代码示例

2.2.1 this为undefined的处理

<button onClick={this.add.bind(this)}>+</button>
or
<button onClick={() => this.add}>+</button>

2.2.2 组件代码

Class出现的原因是需要局部变量,不要全局变量

setState会减少更新损耗

jsbin.com/hiveqagima/…

<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  <script src="https://cdn.bootcss.com/react/16.4.0/umd/react.production.min.js"></script>
  <script src="https://cdn.bootcss.com/react-dom/16.4.0/umd/react-dom.production.min.js"></script>
</head>
<body>
  <div id="root">
  </div>
</body>
</html>

function App(props){
  return (
    <div>
      <Box1 name="frank"/>
      <Box2 name="jack"/>
    </div>
  )
}

class Box1 extends React.Component{
  constructor(props){
    super(props)
    this.state = {
      number: 0
    }
  }
  add(){
    this.setState({
      number: this.state.number + 1
    })
  }
  minus(){
    this.setState({
      number: this.state.number - 1
    })
    
    this.state.number -= 1
    render()
  }
  render(){
    return (
      <div className="red">
        <span>{this.state.number}</span>
        <button onClick={this.add.bind(this)}>+</button>
        <button onClick={this.minus.bind(this)}>-</button>
        {this.props.name}
      </div>
    )
  }
}

class Box2 extends React.Component{
  constructor(props){
    super(props)
    this.state = {
      number: 0
    }
  }
  add(){
    this.setState({
      number: this.state.number + 2
    })
  }
  minus(){
    this.setState({
      number: this.state.number - 2
    })
  }
  render(){
    return (
      <div className="red">
        <span>{this.state.number}</span>
        <button onClick={this.add.bind(this)}>+2</button>
        <button onClick={this.minus.bind(this)}>-2</button>
        {this.props.name}
      </div>
    )
  }
}

render()

function render(){
  ReactDOM.render(
    <App/>,  // React.createElement(App)
    document.querySelector('#root')
  )
}
this.setState((state)=> {
    return ( number: state.number + 1 )
})

3. 组件通信

3.1 父子组件通信

jsbin.com/wikoremuye/…

3.1.1 参数逐层传递下去 props, 祖传

3.1.2 子往上传,需要祖先传函数下来,子孙调用函数传回祖先

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.0/umd/react.production.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.0/umd/react-dom.production.min.js"></script>
  <title>JS Bin</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>
.header{
  display: flex;
  justify-content: center;
}

.track{
  border-bottom: 1px solid black;
}

*{margin:0;padding: 0; box-sizing: border-box;}

.playground{
  border: 1px solid black;
  background: green;
}

body{
  padding: 40px;
}
class App extends React.Component{
  constructor(){
    super()
    this.state = {
      result1: 0,
      result2: 0
    }
    this.t0 = new Date()
  }
  success1(x){
    console.log(x)
    console.log('兔子跑完了,用时')
    let r1 = new Date() - this.t0
    this.setState({
      result1: r1
    })
  }
  success2(x){
    console.log(x)
    console.log('乌龟跑完了,用时')
    let r2 = new Date() - this.t0
    this.setState({
      result2: r2
    })
  }
  render(){
    return (
    <div>
      <div class="header">
        <Time1 result={this.state.result1}/>
        <Time2 result={this.state.result2}/>
       </div>
       <Playground success1={this.success1.bind(this)}
                   success2={this.success2.bind(this)}/>
    </div>
    )
  }
}

// 顶部计时器
function Time1(props){
  return (
  <div>
    <h2>🐇用时</h2>
    <div>{props.result}</div>
  </div>
  )
}
function Time2(props){
  return (
  <div>
    <h2> 🐢用时</h2>
    <div>{props.result}</div>
  </div>
  )
}

// 操场 包含了兔子和乌龟跑道以及计时器功能
function Playground(props){
  let success1 = props.success1
  let success2 = props.success2
  return (
    <div class="playground">
       <Track1 success={success1} />
       <Track2 success={success2}/>
    </div>
  )
}

// 兔子跑道
class Track1 extends React.Component{
  constructor(){
    super()
    let n = 0
    this.state = {
      style: {
        transform: `translateX(${n}%)`
      }
    }
    let timerId = setInterval(()=>{
      n+=25
      this.setState({
        style: {
          transform: `translateX(${n}%)`
        }
      })
      if(n>=100){
        window.clearInterval(timerId)
        this.props.success('我是兔')
      }
    },1000)
  }
  render(){
    return (
      <div>
        <div class="player" style={this.state.style}>🐇</div>
        <div class="track"></div>
      </div>
    )
  }
}


// 乌龟跑道
class Track2 extends React.Component{
  constructor(){
    super()
    let n = 0
    this.state = {
      style: {
        transform: `translateX(${n}%)`
      }
    }
    let timerId = setInterval(()=>{
      n+=20
      this.setState({
        style: {
          transform: `translateX(${n}%)`
        }
      })
      if(n>=100){
        window.clearInterval(timerId)
        this.props.success('我是龟')

      }
    },1000)
  }

  render(){
    return (
      <div>
        <div class="player" style={this.state.style}>🐢</div>
        <div class="track"></div>
      </div>
    )
  }
}


ReactDOM.render(<App></App>, document.querySelector('#root'))
  • 运行结果

3.2 任意组件通信

3.2.1 非单向数据流

  • Son2花钱之后发布,Son3订阅,得到数据,但是每个组件都需要发布和订阅事件,太过繁琐
  • 思路:每个组件花钱后发布,剩余所有组件订阅,才能统一数据
// 家族资产总量
    var money = {
        amount: 1000000
    }

    // 发布订阅
    // 订阅传入方法,发布执行队列,以eventName为数组名
    var fnList = {}  
    var eventHub = {
        // 发布(事件名称,操作数据)
        trigger(eventName, data){
            let fnList = fnList[eventName]
            if(!fnList){return}
            for(let i = 0; i < fnList.length; i++){
                fnList[i](data)
            }
        },
        // 订阅(事件名称,操作方法)
        on(eventName, fn){
            if(!fnList[eventName]){
                fnList[eventName] = [];
            }
            fnList[eventName].push(fn)
        }
    }

    // eventHub.on('我要用钱', function fn1(){})
    // eventHub.trigger('我要用钱', 100)

    class App extends React.Component{
        constructor(){
            super()
        }
        render(){
            return (
                <div className = "root">
                    <BigPapa />
                    <YoungPapa />
                </div>
            )
        }
    }
    class BigPapa extends React.Component{
        constructor(){
            super()
            this.state = {
                money: money
            }
        }
        render(){
            return (
                <div className = "papa"> 大爸 {this.state.money.amount}
                    <Son1/>
                    <Son2/>
                </div>
            )
        }
        
    }
    class YoungPapa extends React.Component{
        constructor(){
            super()
            this.state = {
                money: money
            }
        }
        render(){
            return (
                <div className = "papa"> 小爸 {this.state.money.amount}
                    <Son3/>
                    <Son4/>
                </div>
            )
        }
       
    }
    class Son1 extends React.Component{
        constructor(){
            super()
            this.state = {
                money: money
            }
        }
        render(){
            return(
                <div className="son">儿子1 {this.state.money.amount}
                </div>
            )
        }
    }
    class Son2 extends React.Component{
        constructor(){
            super()
            this.state = {
                money: money
            }
        }
        // 二儿子消费了
        x(){
            money.amount -= 100
            // 二儿子发布花钱事件
            eventHub.trigger('我花钱了', 100)
            this.setState({
                money: money
            })
        }
        render(){
            return(
                <div className="son">儿子2 {this.state.money.amount}
                    <button onClick={()=> this.x()}>消费</button>
                </div>
            )
        }
       
    }
    class Son3 extends React.Component{
        constructor(){
            super()
            this.state = {
                money: money
            }
            // 三儿子订阅 钱被花了
            // 二儿子触发了花钱信息,三儿子监听了花钱信息,如果触发了,就执行
            eventHub.on('我花钱了', (data)=>{
                this.setState({
                    money: money
                })
            })
        }
        render(){
            return(
                <div className="son">儿子3 {this.state.money.amount}
                </div>
            )
        }
       
    }
    class Son4 extends React.Component{
        constructor(){
            super()
            this.state = {
                money: money
            }
        }
        render(){
            return(
                <div className="son">儿子4 {this.state.money.amount}
                </div>
            )
        }
        
    }
    ReactDOM.render(<App/>, document.querySelector('#app'))

3.2.2 单向数据流

  • 组件中不可直接修改金额,只有App组件中存money,一路传下去
  • 思路:不直接修改金额,提出需求,事件处理中心操作金额,金额组件间传递
  • 数据放在顶层,通过事件沟通
 // 家族资产总量
    var money = {
        amount: 1000000
    }

    // 发布订阅
    // 订阅传入方法,发布执行队列,以eventName为数组名
    var fnList = {}  
    var eventHub = {
        // 发布(事件名称,操作数据)
        trigger(eventName, data){
            let fnList = fnList[eventName]
            if(!fnList){return}
            for(let i = 0; i < fnList.length; i++){
                fnList[i](data)
            }
        },
        // 订阅(事件名称,操作方法)
        on(eventName, fn){
            if(!fnList[eventName]){
                fnList[eventName] = [];
            }
            fnList[eventName].push(fn)
        }
    }

    // 事件处理中心
    var x = {
        init(){
            eventHub.on('我想花钱', function(data){
                money.amount -= data
                render()    // 重新渲染
            })
        }
    }

    x.init()

    class App extends React.Component{
        constructor(){
            super()
            this.state = {
                money: money
            }
        }
        render(){
            return (
                <div className = "root">
                    <BigPapa money={this.state.money}/>
                    <YoungPapa money={this.state.money}/>
                </div>
            )
        }
    }

    class BigPapa extends React.Component{
        constructor(props){
            super(props)
        }
        render(){
            return (
                <div className = "papa"> 大爸 {this.props.money.amount}
                    <Son1 money = {this.props.money.amount}/>
                    <Son2 money = {this.props.money.amount}/>
                </div>
            )
        }
        
    }

    class YoungPapa extends React.Component{
        constructor(){
            super()
        }
        render(){
            return (
                <div className = "papa"> 小爸 {this.props.money.amount}
                    <Son3 money = {this.props.money.amount}/>
                    <Son4 money = {this.props.money.amount}/>
                </div>
            )
        }
       
    }

    class Son1 extends React.Component{
        constructor(){
            super()
        }
        render(){
            return(
                <div className="son">儿子1 {this.props.money.amount}
                </div>
            )
        }
    }

    class Son2 extends React.Component{
        constructor(){
            super()
        }
        // 二儿子提出消费需求
        x(){
            eventHub.trigger('我想花钱', 100)
        }
        render(){
            return(
                <div className="son">儿子2 {this.props.money.amount}
                    <button onClick={()=> this.x()}>消费</button>
                </div>
            )
        }
       
    }

    class Son3 extends React.Component{
        constructor(){
            super()
        }
        render(){
            return(
                <div className="son">儿子3 {this.props.money.amount}
                </div>
            )
        }
       
    }

    class Son4 extends React.Component{
        constructor(){
            super()
        }
        render(){
            return(
                <div className="son">儿子4 {this.props.money.amount}
                </div>
            )
        }
        
    }

    function render(){
        ReactDOM.render(<App/>, document.querySelector('#app'))
    }

    render()

4. 引入Redux

4.1 名词释义

  • store:存储数据的地方
  • subscribe:订阅事件
  • reducer: 对数据的操作
  • action: 触发的事件 action + payload
//================store存所有数据================
    // 家族资产总量
    var money = {
        amount: 1000000
    }
    // 账户
    var user = {
        id: 123123,
        nickname: '土豪'
    }

    var store = {
        money: money,
        user: user
    }

    //================eventHub================
    var fnList = {}  
    var eventHub = {
        trigger(eventName, data){
            let fnList = fnList[eventName]
            if(!fnList){return}
            for(let i = 0; i < fnList.length; i++){
                fnList[i](data)
            }
        },
        on(eventName, fn){
            if(!fnList[eventName]){
                fnList[eventName] = [];
            }
            fnList[eventName].push(fn)
        }
    }

    // 事件处理中心
    var x = {
        init(){
            // ==================on 是订阅  subscribe================
            eventHub.on('我想花钱', function(data){
                // =============对数据的变动叫 reducer=================
                store.money.amount -= data
                render()    
            })
        }
    }

    x.init()

    class App extends React.Component{
        constructor(){
            super()
            this.state = {
               store: store
            }
        }
        render(){
            return (
                <div className = "root">
                    <BigPapa money={this.state.store.money}/>
                    <YoungPapa money={this.state.store.money}/>
                </div>
            )
        }
    }

    class BigPapa extends React.Component{
        constructor(props){
            super(props)
        }
        render(){
            return (
                <div className = "papa"> 大爸 {this.props.money.amount}
                    <Son1 money = {this.props.money.amount}/>
                    <Son2 money = {this.props.money.amount}/>
                </div>
            )
        }
        
    }

    class YoungPapa extends React.Component{
        constructor(){
            super()
        }
        render(){
            return (
                <div className = "papa"> 小爸 {this.props.money.amount}
                    <Son3 money = {this.props.money.amount}/>
                    <Son4 money = {this.props.money.amount}/>
                </div>
            )
        }
       
    }

    class Son1 extends React.Component{
        constructor(){
            super()
        }
        render(){
            return(
                <div className="son">儿子1 {this.props.money.amount}
                </div>
            )
        }
    }

    class Son2 extends React.Component{
        constructor(){
            super()
        }
        // 二儿子提出消费需求
        x(){
            // 事件就是action  我想花钱是type 100是payload 荷载
            // ==================action = type + payload==================
            eventHub.trigger('我想花钱', 100)
        }
        render(){
            return(
                <div className="son">儿子2 {this.props.money.amount}
                    <button onClick={()=> this.x()}>消费</button>
                </div>
            )
        }
       
    }

    class Son3 extends React.Component{
        constructor(){
            super()
        }
        render(){
            return(
                <div className="son">儿子3 {this.props.money.amount}
                </div>
            )
        }
       
    }
    class Son4 extends React.Component{
        constructor(){
            super()
        }
        render(){
            return(
                <div className="son">儿子4 {this.props.money.amount}
                </div>
            )
        }
        
    }
    function render(){
        ReactDOM.render(<App/>, document.querySelector('#app'))
    }

    render()

4.2 Redux改写

jsbin.com/gekidatiqi/…

let createStore = Redux.createStore
// state 之前的状态 action 监听的动作 返回新状态
let reducers = (state, action) => {
  state = state || {
    money: {amount: 100000}
  }
  switch (action.type) {
    case '我想花钱':
      return {
        money: {
          amount: state.money.amount - action.payload
        }
      }
    case 'event2':
      break;
    case 'event3':
      break;
    default:
      return state
  }
}
const store = createStore(reducers)

class App extends React.Component {
    constructor(){
    super()
  }
  render(){
    return (
      <div className="root"> 
        <BigPapa money={this.props.store.money} />
        <YoungPapa money={this.props.store.money}/>
      </div>
    )
  }
  
}

class BigPapa extends React.Component{
  constructor(){
    super()
    
  }
  render(){
    return (
    <div className="papa"> 大爸 {this.props.money.amount}
      <Son1 money={this.props.money}/>
      <Son2 money={this.props.money}/>
    </div>
  )
  }
  
}
class YoungPapa extends React.Component{
  constructor(){
    super()
    
  }
  render(){
    return (
    <div className="papa"> 二爸{this.props.money.amount}
      <Son3 money={this.props.money}/>
      <Son4 money={this.props.money}/>
    </div>
  )
  }
  
}
class Son1 extends React.Component{
  constructor(){
    super()
    
  }
  render(){
    return (
    <div className="son"> 儿子1 {this.props.money.amount}</div>
    )
  }
  
}
class Son2 extends React.Component{
  constructor(){
    super()
    
  }
  x(){
    store.dispatch({ type: '我想花钱', payload: 100 })
    
  }
  render(){
    return (
    <div className="son"> 儿子2 {this.props.money.amount}
      <button onClick={()=>this.x()}>消费</button>
    </div>
  )
  }
  
}

class Son3 extends React.Component{
  constructor(){
    super()
  }
  render(){
    return (
    <div className="son"> 儿子3 {this.props.money.amount}</div>
    )
  }
  
}
class Son4 extends React.Component{
  constructor(){
    super()
    
  }
  render(){
    return (
    <div className="son"> 儿子4 {this.props.money.amount}</div>
  )
  }
  
}

function render(){
  ReactDOM.render(<App store={store.getState()}/>, document.querySelector('#app'))
}

// 最后订阅发布
render()
store.subscribe(function(){
  render()
})

5. Redux详解

5.1 Redux in VanilliJS

// 1.创建一个全局store
var store = Redux.createStore(更新store的函数-counter)
// 2. 操作store的变化,根据操作生成新的state
function counter(之前的状态-state, 操作-action){ 
	return 新状态-newState 
}
// 3.渲染
render(store)
// 4.页面获取 store.getState()
// store存储state,获取
<span id="value">${store.getState()}</span>
// 5.函数事件中触发变化,更新
// 触发一个事件,派发一个动作,dispatch一个action
store.dispatch({type:'add', payload: 1}) 
// 6.监听变化重新渲染
store.subscribe(()=>{
  render(store)
})

5.2 React + Redux

  • dom diff更新

  • 可不可以统一派发?

  • 但是还得store一层一层往下传

5.2 React + Redux + React-Redux

  • index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { createStore } from 'redux'
import { Provider } from "react-redux";

const reducer = (state, action)=>{
  if(state === undefined){
    return {n: 0}
  }else{
    if(action.type === 'add'){
      var newState = {n: state.n + action.payload}
      return newState
    }else{
      return state
    }
  }
}

const store = createStore(reducer)

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>, 
  document.getElementById('root')
);
  • App.js
import React, { Component } from 'react';
import { connect } from "react-redux";
class App extends Component {
  render() {
    return (
      <div>
        你点击了 <span id="value">{this.props.n}</span><div>
          <button id="add1" onClick={()=> this.props.add1()}>+1</button>
          <button id="add2">+2</button>
          <button id="add1IfOdd">如果是单数就+1</button>
          <button id="add1After2Sec">两秒钟后+1</button>
        </div>
      </div>
    );
  }
}
// 把全局 state  映射给 当前组件的 props
function mapStateToProps(state){
  return {
    n: state.n
  }
}
// 接收一个 Dispatch 映射给 Props 的 add1
function mapDispatchToProps(dispatch) {
  return {
    add1: ()=> dispatch({type:'add', payload: 1})
  }
}
// 导出 connect 之后的 App
export default connect(mapStateToProps, mapDispatchToProps)(App);

6. Context API

6.1 JS以及React的多层传参

  • 缺点:值必须逐层传递,不能断层,如果放全局,又会被修改的风险

6.1.1 JS 中

console.log(1, n1);
f2(n1);
}

function f2(n2) {
console.log(2, n2);
f3(n2);
}

function f3(n3) {
console.log(3, n3);
f4(n3);
}

function f4(n4) {
console.log(4, n4);
}

{
let n = 100;
f1(n);
console.log("done");
}

6.1.2 React 中

import React from "react";
import "./styles.css";

function F1(props) {
  return (
    <div>
      111,{props.n1}
      <F2 n2={props.n1}></F2>
    </div>
  );
}

function F2(props) {
  return (
    <div>
      222,{props.n2}
      <F3 n3={props.n2}></F3>
    </div>
  );
}

function F3(props) {
  return (
    <div>
      333,{props.n3}
      <F4 n4={props.n3}></F4>
    </div>
  );
}

function F4(props) {
  return <div>444,{props.n4}</div>;
}

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 99
    };
  }
  render() {
    return (
      <div>
        <F1 n1={this.state.n}></F1>
      </div>
    );
  }
}

6.2 用Context传值(局部的全局变量)

6.2.1 JS demo

{
  let context = {};
  window.setContext = function(key, value) {
    context[key] = value;
  };
  window.f1 = function f1() {
    console.log(1);
    f2();
  };

  function f2() {
    console.log(2);
    f3();
  }

  function f3() {
    console.log(3);
    f4();
  }
  // 不需要逐级传递,局部访问的全局变量
  function f4() {
    console.log(4, context["n"]);
  }
}

{
  window.setContext("n", 100);
  setTimeout(() => {
    window.f1();
  }, 1000);

  console.log("done");
}

6.2.2 React 中

  • 创建一个Context: const nContext = React.createContext();

  • 提供:<nContext.Provider value={this.state.x}></nContext.Provider>

  • 使用:<nContext.Consumer></nContext.Consumer>

  • codesandbox.io/s/m7pojr19x…

import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";

function F1(props) {
  return (
    <div className="bordered">
      1111
      <F2 />
    </div>
  );
}
function F2(props) {
  return (
    <div className="bordered">
      2222
      <F3 />
    </div>
  );
}
function F3(props) {
  return (
    <div className="bordered">
      3333
      <nContext.Consumer>
        {x => <F4 n4={x.n} setN={x.setN} />}
      </nContext.Consumer>
    </div>
  );
}
function F4(props) {
  return (
    <div className="bordered">
      4444, {props.n4}
      <button onClick={props.setN}>Click me</button>
    </div>
  );
}

const nContext = React.createContext();

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      x: {
        n: 67,
        setN: () => {
          this.setState({
            x: {
              ...this.state.x,
              n: this.state.x.n + 1
            }
          });
          console.log("执行完毕");
        }
      }
    };
  }
  render() {
    return (
      <div>
        <nContext.Provider value={this.state.x}>
          <F1 />
        </nContext.Provider>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

6.2.3 props.children 如何做一个接受函数的组件