阅读视图

发现新文章,点击刷新页面。

React 组件通讯全攻略:拒绝 "Props" 焦虑,掌握数据流动的艺术

前言

React 的开发旅程中,组件就像是乐高积木,我们把它们一个个搭起来构建出复杂的页面。但光搭起来还不够,这些积木之间必须有“电流”流通——这就是数据。
React 的设计哲学是单向数据流,就像瀑布一样,水流默认只能从高处流向低处。但在实际业务中,我们经常需要逆流而上,或者在两个平行的水池间交换水流。今天我们就来盘点 React 中最主流的四种“引水”方式,打通你的组件经脉。


1. 父传子:顺流而下的 Props

这是 React 中最自然、最基础的通信方式。想象一下,父亲给孩子零花钱,父亲(父组件)只需要要把钱(数据)递过去,孩子(子组件)伸手接着就行。
在代码层面,父组件通过在子组件标签上写上自定义属性(msg)来传递数据。

import Child from "./Child"
export default function Parent() {
  const state = {
    name: '小饶'
  }
  
  return (
    <div>
      <h2>父组件</h2>
      {/* 父亲把 '小饶' 这个名字打包进 msg 属性传给孩子 */}
      <Child msg={state.name} />
    </div>
  )
}

孩子组件这边,所有接收到的礼物都装在一个叫 props 的盒子里。不过要注意,Props 是只读的——这意味着孩子只能使用这些数据,不能直接修改它。就像孩子不能自己修改父亲银行卡的余额一样,如果要改,必须请求父亲操作。

export default function Child(props) {
  // 打开盒子看看收到了什么
  console.log(props);
  
  return (
    <h3>子组件 -- {props.msg}</h3>
  )
}

2. 子传父:给孩子一个“遥控器”

既然水流默认向下,那子组件想要改变父组件的数据该怎么办?比如,孩子想告诉父亲:“我考了 100 分,请更新一下你的心情状态”。
这时候,父组件需要提前准备一个“遥控器”——也就是一个函数。父组件把这个函数通过 props 传给子组件,当子组件需要通信时,就按下这个遥控器(调用函数),并把数据作为参数传回去。 **父组件: 准备好 getNum 函数,用来接收数据并更新自己的 count。 **

import Child from "./Child"
import { useState } from 'react'

export default function Parent() {
  let [count, setCount] = useState(1)  

  // 这就是那个“遥控器”函数
  const getNum = (n) => {
    setCount(n)
  }

  return (
    <div>
      <h2>父组件二 -- {count}</h2>
      {/* 把遥控器交给孩子 */}
      <Child getNum={getNum}></Child>
    </div>
  )
}

子组件: 在合适的时机(比如点击按钮时),通过 props 拿到并按下这个“遥控器”。

export default function Child(props) {
  
  const state = {
    num: 100
  }

  function send() {
    // 此时调用的是父组件里的函数,把 100 传了回去
    props.getNum(state.num)
  }

  return (
    <div>
      <h3>子组件二</h3>
      <button onClick={send}>发送</button>
    </div>
  )
}

3. 兄弟组件:找个共同的“家长”

兄弟组件之间没有直接的连线,就像你和你的表弟住在不同的屋子里,想聊天得通过大厅里的长辈传话。 这种模式在 React 中通常被称为状态提升。既然 Brother1 想要给 Brother2 传值,那我们就把这个值保存在他们共同的父亲身上。

  • Brother1 -> Parent:Brother1 先把数据传给父亲(利用上面的子传父技巧)。
  • Parent -> Brother2:父亲拿到数据后,更新自己的状态,再把这个新状态顺手传给 Brother2(利用父传子技巧)。

父组件(中间枢纽):

import { useState } from "react"
import Child1 from "./Child1"
import Child2 from "./Child2"

export default function Parent() {
  let [message, setMessage] = useState()

  // 接收老大传来的消息
  const getMsg = (msg) => {
    setMessage(msg)
  }

  return (
    <div>
      <h2> 父组件三 </h2>
      {/* 接收者:从 Child1 收信 */}
      <Child1 getMsg={getMsg} />
      {/* 发送者:把信转交给 Child2 */}
      <Child2 message={message} />
    </div>
  )
}

Child1(消息发送方):

export default function Child1(props) {
  const state = {
    msg: '1 中的数据'
  }

  function send() {
    props.getMsg(state.msg)
  }
  
  return (
    <div>
      <h3>子组件1</h3>
      <button onClick={send}>1</button>
    </div>
  )
}

Child2(消息接收方):

export default function Child2(props) {
  return (
    <div>
      {/* 坐等父亲把兄弟的消息送过来 */}
      <h3>子组件2 --- {props.message}</h3>
    </div>
  )
}

4. 跨代组件通信:Context 传送门

如果组件层级很深,比如“爷爷 -> 爸爸 -> 儿子 -> 孙子”,如果还用 Props 一层层传,那中间的爸爸和儿子就成了无辜的“搬运工”,代码会变得非常臃肿麻烦。
为了解决这个问题,React 提供了一个 Context(上下文)机制。这就像在家族里设立了一个“广播站”,爷爷在顶层广播,底下的任何一代子孙,只要想听,就可以直接接收信号,完全不需要中间人转手。
爷组件(数据源头):
我们需要先 createContext 创建一个信号塔,然后用 <Context.Provider> 把所有后代包起来,value 就是我们要广播的数据。

import Parent from "./Parent"
import { createContext } from 'react'

export const Context = createContext()  // 1. 建立信号塔

export default function Grand() {

  return (
    <div>
      <h2> 爷组件 </h2>
      {/* 2. 发射信号,内容是 value 中的数据 */}
      <Context.Provider value= {'爷组件的数据'}>
        <Parent/>
      </Context.Provider>
    </div>
  )
}

父组件(路人甲):
你看,父组件完全不需要碰这些数据,它只需要安静地渲染它的子组件即可。

import Child from "./Child"
export default function Parent() {

  return (
    <div>
      <h3>父组件</h3>
      <Child></Child>
    </div>
  )
}

孙子组件(数据接收者):
孙子组件不需要管它离爷爷隔了多少代,直接用 useContext 这个钩子函数,就能连上信号塔拿到数据。

import { useContext } from 'react'
import { Context } from './Grand'  // 3. 引入信号塔定义

export default function Child() {
  // 4. 接收信号
  const msg = useContext(Context)

  return (
    <div>
      <h4>孙子组件 --- {msg}</h4>
    </div>
  )
}

结语

组件通信是 React 开发中最基本也最重要的内功。

  • 简单的父子关系,PropsCallback 是最轻量的选择;
  • 兄弟组件,记得找共同的父级帮忙周转;
  • 当层级太深感到繁琐时,Context 就是你的救星。

掌握了这四招,你就能从容应对绝大多数的组件交互场景,让数据在你的应用中流动得井井有条。

❌