React中组件间的通信方式

360 阅读3分钟

特别注意

  • 通过学习React组件间的通信方式,笔者总结以下两个重要的知识点
    • 高层级传递给低层级—高层级用非函数
      • 如:父传子
    • '' 低层级传递给高层级 ''—高层级用函数(callback)
      • 如:子传父

props

  • 传递数据的对象是一般组件,倾向于父子组件间 img

import React, { Component } from 'react'
{/* 
  1. Props组件传递函数给Son1组件 
  2. Son1组件添加用户后调用传递过来的函数,通过setState()修改Props组件的状态
  3. Props组件重新调用render生命周期函数,然后将更新后的状态传递给Son2组件进行渲染
*/}

{/* Props组件负责存储状态 */}
export default class Props extends Component {
  
  state = {
    users: [
      {name: "hao",age: 20,id: "1"},
      {name: "moon",age: 21,id: "2"},
    ]
  }
  // 传递给Son1组件的回调函数
  updateState = (newUser) => {
    // 将获取到Son1组件调用此函数的参数进行相应的更新
    this.setState({
      users: [...this.state.users,newUser]
    })
  }

  render() {
    return (
      <div>
        {/* Son1组件负责添加一个用户 */}
        <Son1 updateState={this.updateState}/>
        {/* Son2组件负责渲染所有用户 */}
        <Son2 users={this.state.users}/>
      </div>
    )
  }
}

class Son1 extends Component {

  nameRef = React.createRef()
  ageRef = React.createRef()

  addUser = () => {
    let name = this.nameRef.current.value
    let age = this.ageRef.current.value
    let id = "3"
    this.props.updateState({
      name,
      age,
      id
    })
  }

  render() {
    return (
      <div>
        姓名:<input type="text" name="name" ref={this.nameRef}/>
        年龄:<input type="text" name="age" ref={this.ageRef}/>
        <button onClick={this.addUser}>添加一名用户</button>
      </div>
    )
  }
}

class Son2 extends Component {
  render() {
    // console.log(this.props)
    return (
      <div>
        <ul>
          {
            this.props.users.map(ele => {
              return <li key={ele.id}>{ele.name}-{ele.age}</li>
            })
          }
        </ul>
      </div>
    )
  }
}
  • 而且父组件可以通过ref来获取到子组件的vdom,从而得到子组件的实例对象
import React,{ Component } from 'react'
{/*
  1. Ref组件通过jsx标签的ref属性来获取Son组件的实例对象
  2. 然后就可以在Ref组件中使用Son组件的变量和方法
*/}
export default class Ref extends Component {
    
  render() {
    return (
      <div>
        <h1>父组件</h1>
        <Son ref={cNode => (this.componentExample = cNode)}/>    
      </div>
    )
  }
  
  componentDidMount() {
    let {state,sonFunc} = this.componentExample
    let {data} = state
    alert(`父组件查看子组件数据:${data}`)
    sonFunc()
  }
  
}

class Son extends Component {
  
  state = {
    data: 'sonCompoent'
  }
  sonFunc = () => {
    alert('父组件调用子组件的函数(sonFunc)')
  }

  render() {
    return (
      <h1>子组件</h1>
    )
  }
}

消息订阅与发布

  • 倾向于兄弟组件(非嵌套组件)
    • 如:PubSubJS
      • 接受数据的组件 订阅消息
      • 传输数据的组件 进行相应的消息发布
      • 订阅消息的组件 卸载时执行的componentWillUnmount函数体内取消订阅

Context

  • 使用同一个Context对象,由Provider提供数据给它包裹的所有组件-即Consumer
    • 所有组件均可,倾向于跨级组件
    • 最好用一个单独文件导出创建的Context对象
// 1. 创建并导出Context对象
import React from 'react'  
export const MyContext = React.createContext("defaultValue")
import { MyContext } from './MyContext'
export default class Context extends Component {
  
  state = {
    msg: "Context-倾向于跨级传递或兄弟组件"
  }

  render() {
    return (
      <div>
      {/* 2. 用Provider包裹需要渲染的跨级组件 */}
        <MyContext.Provider value={this.state.msg}>
          <ContextSon/>
        </MyContext.Provider>
      </div>
    )
  }
}''
class ContextSon extends Component {
  
  render() {
    return (
      <ContextSonSon/>
    )
  }

}
  • 接收数据的组件为class component
class ContextSonSon extends Component {
  // 声明此静态属性并接收context后才能获取
  static contextType = MyContext

  render() {
    return (
      <div>
        <h4>
          <span>用this.context获取:{this.context}</span>
          <hr/>
          <span>用Consumer获取:</span>
          <MyContext.Consumer>
            {
              value => (<span>{value}</span>)
            }
          </MyContext.Consumer>
        </h4>
      </div>
    )
  }

}
  • 接收数据的组件为function component
function ContextSonSon() {
  return (
    <div>
      <h4>
        <span>用Consumer获取:</span>
        <MyContext.Consumer>
          {
            value => (<span>{value}</span>)
          }
        </MyContext.Consumer>
      </h4>
    </div>
  )
}

RenderProps

render

  • 子组件中预留组件,而此预留组件由父组件来确定并将子组件传递的数据给此预留组件(React)
    • Vue-Slot
import React, { Component } from 'react'

export default class RenderProps extends Component {
  render() {
    return (
      <div>
        <Son render={(data)=>(<OtherComponent msg={data}/>)}/>
      </div>
    )
  }
}

class Son extends Component {
  
  state = {
    msg: `Son组件预留一个组件位置,
          RenderProps组件来确定需要渲染的组件并获取Son组件传递过来的信息(Vue-Slot)`
  }

  render() {
    return (
      <div>
        <h3>-Son</h3>
        {this.props.render(this.state.msg)}
      </div>
    )
  }
}

// 不是比Son组件层级高的组件的 其它组件
class OtherComponent extends Component {
  render() {
    return (
      <h3>
        - OtherComponent<br/>
        -- {this.props.msg}
      </h3>
    )
  }
}

children

  • 利用 this.props.children 存储一般组件的文体内容
  

import React, { Component } from 'react'

export default class RenderProps extends Component {
  render() {
    return (
      <div>
        <h3>利用children属性,Son组件不能给OtherComponent传递数据,但仍然预留了一个组件等待RenderProps组件来确定</h3>
        <Son>
          <OtherComponent/>
        </Son>
      </div>
    )
  }
}

class Son extends Component {
  
  render() {
    return (
      <div>
        <h3>Son</h3>
        {this.props.children}
      </div>
    )
  }
}

// 不是比Son组件层级高的组件的 其它组件
class OtherComponent extends Component {
  render() {
    return (
      <h3>
        OtherComponent
      </h3>
    )
  }
}

redux

  • 集中式管理状态,此处不详细介绍它了,推荐查看react-redux和redux的官方文档
    • 倾向于跨级组件间需要共享状态

参考