React组件间通信

2019-12-27  本文已影响0人  Lethe35

不借助redux等状态管理工具的React组件间的通信解决方法

组件通信分类

React组件间通信分为2大类,3种情况:

父->子(props)

// 父组件 Parent.js:
import React,{ PureComponent } from "react";
import Children from "./Children.js";

export default class Parent extends PureComponent{
  state = {
    title: '父组件',
  }
  outputDesc = () => {
    console.log('父组件向子组件通信,通过props传递参数或者函数');
  }
  render(){
    const { title } = this.state;
    const childProps = {
      title,
      onOutputDesc: this.outputDesc,
    }
    return(
      <Children {...childProps} />
    )
  }
}
// 子组件 Children.js:
// 界面上会显示出 【这是子组件获取到的父组件的title:父组件】
// 点击控制台会打印出 【父组件向子组件通信,通过props传递参数或者函数】
export default class Children extends PureComponent{
  render(){
    const { title, outputDesc } = this.props;
    return(
      <Fragment>
        <p>这是子组件获取到的父组件的title:{title}</p>
        <button onClick={onOutputDesc}>点击</button>
      </Fragment>
    )
  }
}

使用 React提供的context API(Provider 和 Consumer)
对于方法一,如果要通信的组件间的层次结构很深,中间的每一层组件都要去传递 props,增加了复杂性,并且这些 props 并不是这些中间组件自己所需要的。
当组件层次在三层以上时建议使用context,context做的事情就是创建一个上下文对象,并且对外暴露提供者(通常在组件树中上层的位置)和消费者,在上下文之内的所有子组件,
都可以访问这个上下文环境之内的数据,并且不用通过props。可以理解为有一个集中管理state的对象,并限定了这个对象可访问的范围,在范围之内的子组件都能获取到它内部的值。

// context.js
// Provider和Consumer总是成对出现
import React from 'react'
const demoContext = React.createContext();
const demoProvider = demoContext.Provider;
const demoConsumer = demoContext.Consumer;
export demoProvider;
export demoConsumer;

// 父组件
import React from 'react'
import List from './List'
import { demoProvider } from './context'

export default class Todo extends React.PureComponent {
  state = {
      list: [],
  }
  task = React.createRef()
  handleClick = () => {
      const list = [...this.state.list, this.task.current.value];
      this.setState({ list });
      this.task.current.value = '';
  }
  deleteTask = (index) => {
    const { list } = this.state;
    list.splice(index, 1);
    this.setState({ list });
  }
  render() {
    return (
      // 在父(祖先)级组件中把要传递内容放到value里面
      <demoProvider value={{deleteTask: this.deleteTask}}>
          <input type="text" ref={this.task}/>
          <button onClick={this.handleClick}> 添加 </button>
          <List list={this.state.list} deleteTask={this.deleteTask}/>
      </demoProvider>
    );
  }
}
// 子组件
import React from 'react'
import {demoConsumer} from './context'
export default class List extends React.PureComponent{

  render() {
    const { list } = this.props
    return (
      <demoConsumer>
        // 后代组件中的组件放在Consumer里面, 内部是一个函数, 这个函数接受一个对象作为参数, 参数是Provider里面提供的值
        {
          ({ deleteTask }) => {
            return list.map((item, index) => {
              return (
                <li key={item}>
                  { item }
                  <button onClick={()=>{deleteTask(index)}}> 删除 </button>
                </li>
              )
            })
          }
        }
      </demoConsumer>
    );
  }
}

子->父(回调)

父组件将一个函数作为 props 传递给子组件,子组件调用该回调函数,向父组件通信。

// 父组件 Parent.js:
import React,{ PureComponent, Fragment } from "react";
import Children from "./Children.js";

export default class Parent extends PureComponent{
  state = {
    description: '',
  }
  handleChangeDesc = (props) => {
    this.setState({description: '子组件向父组件通信,通过回调传递参数: 在子组件中点击按钮改变父组件state中的属性})
  }
  render(){
    const { description } = this.state;
    const childProps = {
      onChangeDesc: this.handleChangeDesc,
    }
    return(
      <Fragment>
        <p>{description}</p>
        <Children {...childProps} />
      </Fragment>
    )
  }
}

// 子组件 Children.js:
// 界面上会显示出更新后的description值 【子组件向父组件通信,通过回调传递参数: 在子组件中点击按钮改变父组件state中的属性】
export default class Children extends PureComponent{
  changeParentDesc = () => {
    const { onChangeDesc } = this.props;
    onChangeDesc({description: '子组件向父组件通信,通过回调传递参数: 在子组件中点击按钮改变父组件state中的属性'});
  }
  render(){
    const { title, outputDesc } = this.props;
    return(
      <button onClick={this.changeParentDesc}>点击</button>
    )
  }
}

兄弟组件间

方法一:利用共同的上级组件作为中间人进行通信(子1->父->子2)

方法二:通过自定义发布-订阅模式的事件实现(以Node.js中events模块的EventEmitter 类为例,这个会在我的另一篇文章中细讲)

上一篇 下一篇

猜你喜欢

热点阅读