任意组件通信

发布-订阅模式

参考父子组件通信,如果兄弟组件通信的话,应该是子A传父-父传子B-子B传父-父传子A。如果堂兄弟…
很麻烦,所以使用发布-订阅模式。
假设有一个变量,在家族树里的每个人都需要用到。那么如果 成员 A 需要操作这个变量,就发布一个事件,说:”我要改这个变量了”。
我们加一个人进来,观察这个公有变量的动态,一旦有人发布了 “我要改这个变量了” 这个事件,那么就做出相应的响应——修改这个变量,然后再把信息发布给父组件,由父组件把这个新变量层层传递下去,从而避免了子传父父再传子的尴尬局面。
Demo
下面实现一个简单的发布-订阅模式

  1. 写一个 eventHub :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var fnLists = {}
    var eventHub = {
    trigger(eventName, data) { // 发布
    let fnList = fnLists[eventName]
    if(!fnList) { return }
    for(let i = 0; i<fnList.length; i++) {
    fnList[i](data)
    }
    },
    on(eventName, fn) { // 订阅
    if(!fnLists[eventName]) {
    fnLists[eventName] = []
    }
    fnLists[eventName].push(fn)
    }
    }
  2. 添加全局变量和事件处理函数
    事件处理函数用来监听(订阅)事件并作出响应,通过管家来修改变量并更新页面,而不允许其他的组件擅自修改。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var money = {
    amount: 10000
    }
    var x = {
    init() {
    eventHub.on("pay", function(data) { // 订阅事件 "pay"
    money.amount -= data
    render() // 更新页面
    })
    }
    }
    x.init()
  3. 组件内部发布事件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Son1 extends React.Component {
    pay() {
    eventHub.trigger("pay", 100) // 发布事件 "pay"
    }
    render() {
    return (
    <div className="son">amount: {this.props.amount}
    <button onClick={this.pay}>付款100</button>
    </div>
    )
    }
    }

redux

redux 帮助我们进行状态管理,在这里可以看做是发布-订阅模式的一个实现。
它将变量声明和处理函数统一放到了 store 里边,然后使用 store.subscribe() 来订阅事件,使用 store.dispatch({type: “事件类型”, payload: “参数” }) 来发布事件。
使用 redux 改写上面的 demo

  1. redux 帮我们封装了一个 eventHub ,所以不用写。但是要npm install redux 来下载它。
  2. 添加 store (添加全局变量和事件处理函数)。这里的事件处理函数会我们只需要关心值的变化,redux 会帮我们重新 render 页面。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { createStore } from "redux";
    function money(state, action) {
    if (typeof state === "undefined") {
    return {
    amount: 10000
    };
    }
    switch (action.type) {
    case "pay":
    return { amount: state.amount - action.payload };
    default:
    return state;
    }
    }
    var store = createStore(money);

其中 money 的参数 state 是我们要使用的全局变量,而 action 是事件的类型,根据事件类型选择处理函数。所有的处理函数和全局变量都封装在了这里。通过 store.getState().amount 来获取 amount 的值。

  1. dispatch 事件(组件内部发布事件)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Son1 extends React.Component {
    pay() {
    store.dispatch({ type: "pay", payload: 100 }); // 发布事件 "pay"
    }
    render() {
    return (
    <div className="son">amount: {this.props.amount}
    <button onClick={this.pay}>付款100</button>
    </div>
    )
    }
    }

Context

Context 给我们创造一个局部的全局变量(只在 provider 内部的 component 里算是全局变量,其他地方无法访问),同样可以用来做组件通信。使用 context 修改的 demo

  1. const MoneyContext = React.createContext({}); 创建一个 context。其中 {} 是初始化这个 context 的值。
  2. 提供一个你需要用的变量,和修改这个变量的方法。这里我们在根组件上设置 state 的值。这里因为是 state 里的值,在 setState 完成之后 react 会帮我们重新 render 页面,所以也只需要关心值的变化。

    1
    2
    3
    4
    5
    6
    7
    8
    this.state = {
    amount: 10000,
    pay: (amount) => {
    this.setState({
    amount: this.state.amount - amount
    });
    }
    };

    然后将 this.state 通过 Provider 的 value 属性传出去

    1
    2
    3
    4
    5
    6
    <MoneyContext.Provider value={this.state}>
    <div>
    <Dad1 />
    <Dad2 />
    </div>
    </MoneyContext.Provider>
  3. 在需要调用的地方使用 Consumer 接收这个值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <MoneyContext.Consumer>
    {(value) => (
    <div className="dad">
    Dad1 amount: {value.amount}
    <button onClick={() => { value.pay(10); }}>
    付款10
    </button>
    <Son1 />
    <Son2 />
    </div>
    )}
    </MoneyContext.Consumer>

总的来说

  1. 使用 eventHub/eventBus 来通信。用一个组件来监听某个事件,另一个组件触发这个事件并传递参数,即可实现两个组件的通信。缺点是当事件很多的时候,代码会变得略复杂。
  2. 使用 redux ,每次操作的时候触发一个 action ,这个 action 会根据旧的 state 生成新的 state 。使用 store.subscribe 来监听 state 的变化,一旦 state 变化了就重新 render 页面。
  3. 使用 context ,创造一个局部的全局变量。然后通过 provider 将这个变量和修改变量的函数传递出去。使用 consumer 来接收变量和函数,调用函数从而实现通信。利用 class 组件内部的 this.state 来实现页面的 render 。
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2018-2023 文初阳
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信