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组件通信原理

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

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