React 组件间通信

React作为前端的新一代主流框架,因其组件化的思想,彻底革新了前端停留在DOM操作的古老开发方式。使用React的组件,不再需要模板,也不用再过分担心渲染和更新带来的性能问题。开发一个个组件,就像一个个模块一般,在需要的地方放在那里就好。但是这么多的组件之间,如何进行信息交流和信息传递,就引发了一个新的问题——组件通信。

我是目录

  1. 事件通信
  2. 观察者模式
  3. 关于Redux与Flux

首先,我们先假设我们的组件结构如下:

      Parent
     ____|____
     |       |
  ChildA  ChildB

1. 事件通信

1.1 父传子

React 中,父组件可以向子组件通过传 props 的方式,向子组件进行通讯。

class Parent extends Component {
  state = { msg: 'start' };

  componentDidMount() {
    setTimeout(() => { this.setState({ msg: 'end' }); }, 1000);
  }

  render() {
    // 将传递值当做组件的props属性传递给子组件
    // return <ChildA msg={this.state.msg} />;
    // 使用...运算符将父组件信息以更简洁的方式传递给子组件
    return <ChildA {...this.state} />;
  }
}

class ChildA extends Component {
  render() {
    // 获取父组件传递过来的数据中的msg
    return <p>{this.props.msg}</p>;
  }
}

export default Parent;

1.2 子传父

子组件向父组件通讯,同样也需要父组件向子组件传递 props 进行通讯,只是父组件传递的,是作用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,作为参数,传递到父组件的作用域中。

class Parent extends Component {
  state = { msg: 'start' };

  transferMsg(childMsg) {
    this.setState({ msg: childMsg });
  }

  render() {
    return (
      <div>
        <p>{this.state.msg}</p>
        // 将事件传递给子组件,子组件通过事件传递参数与父组件通信
        <ChildA transferMsg={childMsg => this.transferMsg(childMsg)} />
      </div>
    );
  }
};

class ChildA extends Component {
  componentDidMount() {
    setTimeout(() => { this.props.transferMsg('end'); }, 1000);
  }

  render() {
    return <div />;
  }
}

1.3 兄弟组件通信

对于没有直接关联关系的两个节点,就如 ChildAChildB 之间的关系,他们唯一的关联点,就是拥有相同的父组件。参考之前介绍的两种关系的通讯方式,如果我们向由 ChildAChildB 进行通讯,我们可以先通过 ChildAParent 组件进行通讯,再由 ParentChildB 组件进行通讯。

class Parent extends Component {
  state = { msg: 'parent' };

  transferMsg(childAMsg) {
    this.setState({ msg: childAMsg });
  }

  componentDidUpdate() {
    console.log('Parent is update'); // 测试更新State后哪些组件也被更新了生命周期
  }

  render() {
    return (
      <div>
        <ChildA transferMsg={childAMsg => this.transferMsg(childAMsg)} />
        <ChildB {...this.state} />
      </div>
    );
  }
}

class ChildA extends Component {
  componentDidMount() {
    setTimeout(() => {
      this.props.transferMsg('ChildA');
    }, 1000);
  }

  componentDidUpdate() {
    console.log('ChildA is update'); // 测试更新State后哪些组件也被更新了生命周期
  }

  render() {
    return <div />;
  }
}

class ChildB extends Component {
  componentDidUpdate() {
    console.log('ChildB update'); // 测试更新State后哪些组件也被更新了生命周期
  }

  render() {
    return (
      <div>
        <p>I am ChildB, this is ChildA to parent and then to ChildB: {this.props.msg}</p>
        <ChildBchild />
      </div>
    );
  }
}

class ChildBchild extends Component {
  componentDidUpdate() {
    console.log('ChildBchild is update'); // 测试更新State后哪些组件也被更新了生命周期
  }

  render() {
    return <div />;
  }
}

当我们在浏览器运行时,可以从控制台发现,各个组件的 componentDidUpdate 方法均被触发。所以,有没有更好的解决方式呢?

2. 观察者模式

观察者模式也叫发布-订阅者模式,发布者发布事件,订阅者监听事件并做出反应。我们通过这种模式,在全局定义一个事件代理管理器,每一个组件只需要引入这个事件代理者即可。

事件代理文件,eventBus.js

const eventBus = {
  onObj: {},
  // 事件监听
  on(key, fn) {
    this.onObj[key] === undefined && (this.onObj[key] = []);
    this.onObj[key].push(fn);
  },
  // 事件关闭
  off(key) {
    this.onObj[key] = [];
    this.oneObj[key] = [];
  },
  // 事件触发
  trigger() {
    /*
      备注:
      除了事件参数,其他参数允许传入数组,但对于传入的map结构、函数、以及多个其他参数都没有做处理,
      这点可以根据个人需要进行拓展
    */
    // 无参返回false
    if (arguments.length === 0) {
      return false;
    }
    // key是事件,args是参数 - 通信信息
    const argumentsArr = [...arguments];
    let key = argumentsArr[0];
    let args = argumentsArr.slice(1);

    if (this.onObj[key] !== undefined && this.onObj[key].length > 0) {
      for (let i in this.onObj[key]) {
        this.onObj[key][i].apply(null, args);
      }
    }
  }
};

export default eventBus;

事件代理文件,eventBus.js

class Parent extends Component {
  render() {
    return (
      <div>
        <ChildA />
        <ChildB />
      </div>
    );
  }
}

class ChildA extends Component {
  componentDidMount() {
    let hello = 'ChildA - 结束';
    setTimeout(() => {
      eventBus.trigger('change', hello);
    }, 1000);
  }

  render() {
    return <div></div>;
  }
}

class ChildB extends Component {
  state = {
    msg: 'ChildB - 开始'
  };

  componentDidMount() {
    eventBus.on('change', msg => {
      this.setState({
        msg
      });
    });
  }

  render() {
    return (
      <div>
        <p>ChildA to ChildB component: {this.state.msg}</p>
      </div>
    );
  }
}

3. 关于Redux与Flux

关于 ReduxFlux 就是用来管理状态和解决组件通信问题的。但虽然 Redux 对于组件间的解耦提供了很大的便利,如果你在考虑该不该使用 Redux 的时候,社区里有一句话说,“当你不知道该不该使用 Redux 的时候,那就是不需要的”。Redux 用起来一时爽,重构或者将项目留给后人的时候,就是个大坑,Redux 中的 dispatchsubscribe 方法遍布代码的每一个角落。虽然 Flux 设计中的 Controller-Views 概念就是为了解决这个问题出发的,将所有的 subscribe 都置于 Parent 组件(Controller-Views),由最上层组件控制下层组件的表现,然而,这不就是我们所说的子组件向父组件通讯这种方式了。

参考来源:淘宝-React组件通信原理

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 处理 React 组件之间的交流方式,主要取决于组件之间的关系,然而这些关系的约定人就是你。 React 组件之间...
    落花的季节阅读 3,320评论 0 3
  • 最近学习浅尝则止的学习了一下react.js这个UI的框架,react这个库给我的最大的感觉就是它能够完全的接管U...
    璀璨天宇阅读 527评论 0 0
  • React 开发模式是组件化开发, 所以组件间的信息传递就尤为重要,React传递数据的方式主要有3种。 prop...
    蛮吉大人123阅读 81评论 0 0
  • 这里是记录我在色彩学习中的心得,“色”即为“色”,而“涩”为“青涩”。代表我不成熟的小建议。 亚里士多德是众多学科...
    SpongeBob被用了阅读 2,109评论 0 0
  • 定计划这件事,尽管只是偶尔穿插在人生中,不一定起作用的分叉路口,但是它还是贯穿了我们的一生,一个家庭主妇对于每天吃...
    百事可爱哟阅读 471评论 0 0