从EventHub浅析redux的原理

199 阅读4分钟

在react中,如果我们要实现任意组件之间的通信,我们可以通过redux实现。 在介绍redux之前,我们可以通过EventHub来实现一个redux来帮助我们理解。


首先,我们来看一下我们代码的结构,

容器内有A B两个组件,

A组件内有 C D 两个组件,

B组件内有 E F 两个组件。

代码如下

import React from 'react'
import ReactDOM from 'react-dom'
import style from './style3.css'

class App extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='App'>
        <A/>
        <B/>
      </div>
    )
  }
}

class A extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='A'>
        A
        <C/>
        <D/>
      </div>
    )
  }
}


class B extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='B'>
        B
        <E/>
        <F/>
      </div>
    )
  }
}

class C extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='Son'>
        C
      </div>
    )
  }
}

class D extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='Son'>
        D
      </div>
    )
  }
}

class E extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='Son'>
        E
      </div>
    )
  }
}

class F extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='Son'>
        F
      </div>
    )
  }
}

const render = () => {
  ReactDOM.render(<App/>, document.querySelector('#root'))
}

render()

//css
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

.App, .A, .B {
    border: 1px solid black;
    display: flex;
    justify-content: center;
    padding: 20px;
    margin: 20px;
}

.A, .B {
    flex-direction: column;
    text-align: center;
}

.Son{
    border: 1px solid black;
    padding: 20px;
    margin: 20px;
}

接下来我们假设容器内有100000元,当D组件消费时,其他所有的组件都可以实时更新金额。

const money = {
  amount: 100000
}

class App extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='App'>
        money = {this.props.money.amount}
        <A money={this.props.money}/>
        <B money={this.props.money}/>
      </div>
    )
  }
}

const render = () => {
  ReactDOM.render(<App money={money}/>, document.querySelector('#root'))
}

render()

我们可以将money通过props的方式层层传递,最后形成以下图

EventBus

接下来,为了实现一个组件消费,其他组件实时更新,我们需要一个EventHub来实现订阅发布模式。

const lists = {}

const EventHub = {
  on(eventName, fn) {
    if (!lists[eventName]) {lists[eventName] = []}
    lists[eventName].push(fn)
  },
  trigger(eventName, data) {
    if (!lists[eventName]) {return false}
    lists[eventName].map(item => item(data))
  }
}

我们通过Event.on 监听事件,然后push一个函数

在通过Event.trigger 触发事件,将data传入,执行函数

管家

接下来,我们还需要一个管家来监听事件

const steward = {
  init(){
    EventHub.on('花钱',(data)=>{
      money.amount -= data
      render()
    })
  }
}

steward.init()

我们的管家需要监听'花钱'事件,然后将剩余金额算出来,并重新render实现全局更新数据。

之后,我们就可以在组件D中,触发事件了

class D extends React.Component {
  constructor(props) {
    super(props);
  }

  x=()=>{
    EventHub.trigger('花钱',100)
  }

  render() {
    return (
      <div className='Son'>
        D
        money={this.props.money.amount}
        <button onClick={this.x}>花钱</button>
      </div>
    )
  }
}

此时,我们就实现了任意组件之间的通信了

//全部代码

import React from 'react'
import ReactDOM from 'react-dom'
import style from './style3.css'

const money = {
  amount: 100000
}

const lists = {}

const EventHub = {
  on(eventName, fn) {
    if (!lists[eventName]) {
      lists[eventName] = []
    }
    lists[eventName].push(fn)
  },
  trigger(eventName, data) {
    if (!lists[eventName]) {
      return false
    }
    lists[eventName].map(item => item(data))
  }
}

const steward = {
  init() {
    EventHub.on('花钱', (data) => {
      money.amount -= data
      render()
    })
  }
}

steward.init()

class App extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='App'>
        money = {this.props.money.amount}
        <A money={this.props.money}/>
        <B money={this.props.money}/>
      </div>
    )
  }
}

class A extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='A'>
        A
        money = {this.props.money.amount}
        <C money={this.props.money}/>
        <D money={this.props.money}/>
      </div>
    )
  }
}


class B extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='B'>
        B
        money = {this.props.money.amount}
        <E money={this.props.money}/>
        <F money={this.props.money}/>
      </div>
    )
  }
}

class C extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='Son'>
        C
        money={this.props.money.amount}
      </div>
    )
  }
}

class D extends React.Component {
  constructor(props) {
    super(props);
  }

  x = () => {
    EventHub.trigger('花钱', 100)
  }

  render() {
    return (
      <div className='Son'>
        D
        money={this.props.money.amount}
        <button onClick={this.x}>花钱</button>
      </div>
    )
  }
}

class E extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='Son'>
        E
        money={this.props.money.amount}
      </div>
    )
  }
}

class F extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='Son'>
        F
        money={this.props.money.amount}
      </div>
    )
  }
}

const render = () => {
  ReactDOM.render(<App money={money}/>, document.querySelector('#root'))
}

render()

redux

redux和EventHub的原理基本上是一样的

redux有以下几个关键内容,分别和EventHub对应

redux作用EventHub
store存储数据money
reducer存储eventname,执行操作steward
dispatch 中的 type触发事件trigger中的eventname
dispatch 中的 payload触发事件时传的数据trigger中的data
subscribe数据一旦有变化就重新渲染steward中的render
  1. redux的引入
yarn add redux
  1. 创建store reducer
import {createStore} from "redux"

const reducer = (state, action) => {
  state = state || {money: {amount: 100000}}
  switch (action.type) {
    case '花钱':
      return {
         money: {amount: state.money.amount - action.payload}
      }
    default:
      return state
  }
}

const store =createStore(reducer)
  1. 触发事件
class D extends React.Component {
  constructor(props) {
    super(props);
  }

  x = () => {
    store.dispatch({type: '花钱', payload: 100})
  }

  render() {
    return (
      <div className='Son'>
        D
        money={this.props.money}
        <button onClick={this.x}>花钱</button>
      </div>
    )
  }
}
  1. 订阅+重新渲染
const render = () => {
  ReactDOM.render(<App store={store.getState()}/>, document.querySelector('#root'))
}

store.subscribe(render)

render()

完整代码如下

import React from 'react'
import ReactDOM from 'react-dom'
import style from './style3.css'
import {createStore} from "redux"

const reducer = (state, action) => {
  state = state || {money: {amount: 100000}}
  switch (action.type) {
    case '花钱':
      return {
        money: {amount: state.money.amount - action.payload}
      }
    default:
      return state
  }
}

const store = createStore(reducer)


class App extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='App'>
        money = {this.props.store.money.amount}
        <A money={this.props.store.money.amount}/>
        <B money={this.props.store.money.amount}/>
      </div>
    )
  }
}

class A extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='A'>
        A
        money = {this.props.money}
        <C money={this.props.money}/>
        <D money={this.props.money}/>
      </div>
    )
  }
}


class B extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='B'>
        B
        money = {this.props.money}
        <E money={this.props.money}/>
        <F money={this.props.money}/>
      </div>
    )
  }
}

class C extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='Son'>
        C
        money={this.props.money}
      </div>
    )
  }
}

class D extends React.Component {
  constructor(props) {
    super(props);
  }

  x = () => {
    store.dispatch({type: '花钱', payload: 100})
  }

  render() {
    return (
      <div className='Son'>
        D
        money={this.props.money}
        <button onClick={this.x}>花钱</button>
      </div>
    )
  }
}

class E extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='Son'>
        E
        money={this.props.money}
      </div>
    )
  }
}

class F extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='Son'>
        F
        money={this.props.money}
      </div>
    )
  }
}

const render = () => {
  ReactDOM.render(<App store={store.getState()}/>, document.querySelector('#root'))
}

store.subscribe(render)

render()

redux的优点:

  • 收敛eventname,便于管理
  • 通过props传递store 虽然由于js的特性无法做到避免在组件内部修改store中的数据,大门还是在提醒我们不要随意修改store