React 组件间的通信

发布于 2023-10-05 12:03:56 字数 3807 浏览 40 评论 0

在 React 里, UI = render(state) ,页面是由数据驱动更新的。平时我们写 React 的时候,本质上就是在跟数据打交道,了解清楚数据的流向,对厘清业务是有帮助的。而 React 在数据这方面,推崇的是 单向数据流 ,也就是数据只能由高层级的组件流向低层级的组件。下面介绍几种 React 组件间的数据通信。

父子组件

对于父子组件来说,通常是由父组件定义数据,然后通过 props 传递给子组件。在这种模式下,子组件就是个 无状态组件 ,它的数据全部由父组件定义和控制。当需要更改自身状态时,通过 事件驱动 的方式来更改,即子组件 emit 发送一个事件出去,并把当前的数据状态传递给父组件,父组件接收到后,调用 setState 更新数据,然后页面 UI 随之刷新。

兄弟组件

对于兄弟组件,数据的传递方式是通过 父组件 做中转。假设有兄弟组件 B 和 C,以及它们的父组件 A。此时 B 要向 C 传递数据,怎么做呢?首先,父组件 A 在子组件 B 里绑定一个事件监听函数 fn,子组件 B 通过 fn 函数入参把想传递的数据交给父组件 A,A 拿到数据后,通过 setState 更改 A 组件当前的 state 数据,然后再把这个数据通过 props 的方式传递给子组件 C。这样就实现了兄弟组件通信。

跨组件通信 —— 发布订阅模式

React 单向数据流的好处是数据来源清晰,方便管理,但坏处是假设组件很多的话,通过一层层 props 传递下去, 嵌套太多不是好办法 。所以还有一种传递数据的方式: 发布——订阅模式 。它通过一方发布事件,另一方订阅事件来实现数据的传递。对于发布订阅模式来说,有以下几个重要方法:

  • on
  • off
  • emit
  • once

一个简易的发布——订阅模式实现代码如下:

class EventEmitter {
  constructor() {
    this.events = {}
  }
  
  emit(type, ...args) {
    this.events[type].forEach(fn => {
      fn(...args)
    })
    return true
  }
  
  on(type, handler) {
    this.events[type] = this.events[type] || []
    this.events[type].push(handler)
    return this
  }
  
  off(type, handler) {
    const lis = this.events[type]
    if (!lis) return this
    for (let i = lis.length; i > 0; i--) {
      if (lis[i] === handler) {
        lis.splice(i, 1)
        break
      }
    }
    return this
  }
  
  once(type, handler) {
    this.events[type] = this.events[type] || []
    const onceWrapper = () => {
      handler()
      this.off(type, onceWrapper)
    }
    this.events[type].push(onceWrapper)
    return this
  }
}

Context API

React 本身提供了一个组件间全局通信的方式,那就是 Context API。这个 API 有三个重要概念:

  • React.createContext : 创建一个上下文对象用来保存数据
  • Provider :把组件包裹在 Provider 里,通过 Provider 的 value 属性获取需要传递的数据
  • Consumer : 消费数据的组件,接收定义在 Provider 的 value 数据

Context API 驾驭起来难度大,因此很少被推荐使用,如果是大型复杂项目,数据管理通常会采用第三方状态库 redux

redux

引用官方的描述:

Redux 是 JavaScript 状态容器,它提供可预测的状态管理。

Redux 并不属于 React,而是提出了一套数据管理的思想和规范,React 根据这个思想实现了自己的状态管理库 react-redux
对于 Redux 架构,有三个重要概念需要理解:

  • store : 全局唯一的数据源,它是只读的
  • reducer :是一个纯函数,它根据 action 的动作,来对数据进行分发处理,最后返回新的数据状态,更新 store。store 一旦更新,就驱动视图层刷新,UI 也就改变了,这是个 严格的单一数据流
  • action : 因为 store 是只读的,我们如果想改变数据,就需要定义一个改变「动作」,这个动作描述了新的数据状态信息,它会被 reducer 接收处理。

redux 的工作流是这样:首先,我们需要有一个类似 createStore 的方法来创建我们的唯一数据源 store ,有了 store 后,我们想改变数据,该怎么做?首先,我们需要定义一个 action 动作,描述你这个数据是新增还是修改,比如 action = {type: 'ADD', payload: '张三'} ,定义 action 就是解决 你想要什么

然后,我们还要编写 reducer 纯函数,在里面指定如何来响应 action ,比如上面的 action 是新增一个叫 张三 的名字,那我们就要在函数里根据这个动作,来创建新的数据,这个数据最终会被处理更新到 store 上。现在我们 store 有了,action 也有了,reducer 处理函数也有了,那怎么实现更新到 store 这个操作呢?

这时 dispatch 函数就登场了,当你通过 createStore 方法创建完 store 数据源后,就可以通过 store.dispatch 来触发数据的更新。
总结下 redux 的整个工作流程:

  • 创建唯一数据源 store
  • 定义更改数据的动作 action
  • 定义处理 action 的函数,称作 reducer
  • 通过 store.dispatch 来让 reducer 处理 action

总结

React 遵循单向数据流原则,有以下几种传递数据的方式:

  • props
  • 发布——订阅模式
  • Context API
  • redux

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

文章
评论
27 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文