react组件的五种通信

107 阅读6分钟

开启React开发之旅

在当今前端开发的星辰大海中,React如同璀璨的北极星,指引着无数开发者构建高效、灵活的现代化应用。然而,当我们真正踏上React开发之旅时,组件间的"对话"能力往往成为项目成败的关键!就像建造一栋精密的摩天大楼,每个房间(组件)都需要完美的通信管道。

本文将手把手带你从项目脚手架搭建开始,穿越组件通信的五大核心场景,最后抵达Redux状态管理的专业领域。如果你是刚接触React的新水手,这里都有你需要的航海图和工具箱。让我们启航,掌握React组件间优雅对话的艺术

创建react项目步骤

npm create vite@latest react-redux -- --template react

安装vite

npm i 自动安装所需依赖

安装react插件之后用提示词rfc就可以快捷生成组件

要使用redux仓库的话要先安装 react-redux 和redux的中间商tooklkit

npm install @reduxjs/toolkit react-redux

组件通信

1. 父子组件通信

  • 父组件直接在子组件标签上写属性,子组件通过props接收

import React, { useRef } from "react";
import { useState } from "react";
import Child from "./Child.jsx";
export default function Parent() {
  const [state, setState] = useState(["1", "2", "3"]);
  const inputRef = useRef();    //获取到的是一个对象
  const header=()=>{console.log(inputRef.current.value);
    setState([...state, inputRef.current.value]);
  }
  return (
    <div>
      <div className="hd"></div>
      <input type="text" ref={inputRef} />
      <button onClick={header}>确认</button>
    <Child list={state}></Child>
    </div>
  );
}

通过对父组件里子组件标签添加属性来通信

export default function Child({list}) {//这里以对象键值对的方式来传递需要的参数 对象的解构
  return (
     <div className="bd">
        <ul>
          {list.map((item)=>{
            return <li key={item}>{item}</li>
          })}
        </ul>
      </div>
  )
}

2. 子父组件通信

  • 父组件创建一个函数,将该函数传递给子组件,子组件调用该函数并将变量作为参数传入该函数
import { useRef } from "react";
export default function Child({setState,state}) {
  const inputRef = useRef(null); //获取到的是一个对象 //如果不引入 useRef 会无法显示组件但不会报错
  const header = () => {
    setState([...state, inputRef.current.value]);//将input框里的值添加到state里

  ;
  };
  //这里以对象键值对的方式来传递需要的参数 对象的解构
  return (
    <div className="hd">
      <input type="text" ref={inputRef} />
      <button onClick={header}>确认</button>
    </div>
  );
}

import { useState } from "react";
import Child from "./Child.jsx";
export default function Parent() {
  const [state, setState] = useState(["1", "2", "3"]);

  return (
    <div>
      <Child setState={setState} state={state} />
      <div className="bd">
        <ul>
          {state.map((item) => {
            return <li key={item}>{item}</li>;
          })}
        </ul>
      </div>
    </div>
  );
}

为什么能进行子传父呢?因为通过标签向子组件传递了一个函数,类型为引用类型,引用类型数据是保存在堆里面的,所以传递给子组件的只是引用数据的地址,子组件拿到函数和传过来的参数就可以对原来父组件的参数进行修改而不需要向父组件返回参数 3. 兄弟组件通信

  • 通过子传父,再通过父传子 实现
import Child1 from "./Child1";
import Child2 from "./Child2";
import { useState } from "react";


export default function () {
   const [list, setList] = useState(['html', 'css', 'js'])

   
  return (
    <div>
      <Child1 setList={setList}></Child1>
      <Child2 list={list}></Child2>
     
    </div>
  );
}


import { useRef } from "react";

export default function Child({ setList }) {
  const inputRef = useRef();

  const handler = () => {
    setList((list) => {
      return[...list, inputRef.current.value];
    });
  };

  return (
    <div className="hd">
      <input type="text" ref={inputRef} />
      <button onClick={handler}>确认</button>
    </div>
  );
}
import { useEffect } from "react";
import { useState } from "react";
export default function Child({ list }) {
  return (
    <div className="bd">
      <ul>
        {list.map((item) => {
          return <li key={item}>{item}</li>;
        })}
      </ul>
    </div>
  );
}

其实向子组件传递的setList自带了list参数代表原来父组件的list,不需要我们再单独传递 setList((list) => { return[...list, inputRef.current.value];对原数组进行解构,加上input输入框里的值,返回新数组 });

4. 跨层级组件通信

  • 假设组件A是组件B的父组件,组件B是组件C的父组件。要将A中的数据直接交给C使用,需要创建一个全局的Context组件,
  • 组件A中使用Context.Provider 标签包裹组件B
  • 组件C中使用useContext 函数获取Context中的数据
import React, { use } from "react";
import { useState, createContext } from "react";
import { useContext } from "react";
const myContext = createContext();//创建一个上下文对象


function A() {
  return (
    <div>
      <h2 >AAA</h2>
      <B></B>
    </div>
  );
}

function B() {
  const msg=useContext(myContext);
  return <div>
    <h3>BBB---{msg}</h3>
  </div>;
}
export default function index() {
  const [msg, setMsg] = useState("Index组件中的数据");

  return (
    <div>
      <myContext.Provider value={msg}>
        <A />
      </myContext.Provider >
    </div>
  );
}

用createContext()全局创建一个发射器对象,使用useContext钩子函数把全局发射器对象使用起来,我们只需要在最外层的根组件用发射器对像.Provider标签来包裹子组件标签,再在标签中添加属性就可以向孙子组件传递

5. 任意组件通信

  • redux
  1. @reduxjs/toolkit (创建仓库)
  2. react-redux (把仓库和react关联起来)
  3. 创建一个总仓库,在总仓库中注册各个子模块,子模块通常创建在modules目录下.使用仓库中的数据,需要使用useSelector 函数获取数据,修改仓库中的数据,需要:
    • 调用仓库中的方法 得到一个action行为
    • 调用dispatch 函数将action 行为 传递给仓库
1. Redux 架构

Redux 是 JavaScript 应用的状态管理容器,核心思想:

  • 单一数据源:整个应用状态存储在唯一 store 中
  • 状态只读:只能通过 dispatch action 修改状态
  • 纯函数修改:Reducer 必须是纯函数(无副作用)

2. Redux Toolkit 核心 API
// 创建 slice 的完整结构
const xxxSlice = createSlice({
  name: '模块名',        // 命名空间(必填)
  initialState: 初始值,   // 初始状态
  reducers: {            // 同步 reducer 集合
    动作名(state, action) {
      // 直接修改 state(Immer 支持)
    }
  },
  extraReducers(builder) {
    // 处理其他 slice/异步的 action
  }
})

3. 代码解析
(1) Counter 模块
const counter = createSlice({
  name: 'counter',
  initialState: {
    count: 0,
    list: ['html', 'css', 'js']
  },
  reducers: {
    // 直接修改 state(Immer 代理)
    add(state) {
      state.count++  // 
    },
    // 带参数的 action
    listadd(state, action) {
      state.list.push(action.payload)
    }
  }
})

// 导出 action creators
export const { add, listadd } = counter.actions
// 导出 reducer
export default counter.reducer
(2) TodoList 模块
const todoList = createSlice({
  name: "todoList",
  initialState: {
    list: [
      {id:1, title:"todo1", completed:false},
      {id:2, title:"todo2", completed:false}
    ]
  },
  reducers: {} // 可扩展
})

export default todoList.reducer

4. 项目整合
创建 Store (store.js)
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './counter'
import todoReducer from './todoList'

export default configureStore({
  reducer: {
    counter: counterReducer,  // 访问路径:state.counter
    todoList: todoReducer     // 访问路径:state.todoList
  }
})
连接 React (index.js)
import { Provider } from 'react-redux'
import store from './store'

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

5. 组件中使用
获取状态
import { useSelector } from 'react-redux'

function Component() {
  const count = useSelector(state => state.counter.count)
  const todos = useSelector(state => state.todoList.list)
  
  return (
    <div>Count: {count}</div>
    <ul>
      {todos.map(todo => <li key={todo.id}>{todo.title}</li>)}
    </ul>
  )
}
修改状态
import { useDispatch } from 'react-redux'
import { add, listadd } from './counterSlice'

function Component() {
  const dispatch = useDispatch()

  return (
    <button onClick={() => dispatch(add())}>+1</button>
    <button onClick={() => dispatch(listadd('react'))}>
      添加 React
    </button>
  )
}

6. 执行流程
  1. 组件 dispatch(action)
  2. Store 调用对应 slice 的 reducer
  3. Reducer 返回新状态(旧状态 + action)
  4. Store 更新状态并通知组件
  5. 组件通过 selector 获取新值并重渲染

通过 createSlice + configureStore + React-Redux Hooks,可以高效实现全局状态管理。你的代码已正确实现模块化结构,接下来可扩展异步操作和组件连接。

至此,我们已经完整探索了React开发的完整链路:从npm create vite的初始化魔法,到父子组件间的属性传递;从子组件逆向通信的回调技术,到兄弟组件的状态接力;最后跨越组件层级,用Context API和Redux Toolkit架起任意组件间的通信桥梁。

总结:

1️⃣ 父子通信是基础 - props传递如同组件间的"生命线"
2️⃣ 子父通信需技巧 - 回调函数是逆向沟通的密钥
3️⃣ 兄弟对话靠中介 - 状态提升到共同父组件
4️⃣ 跨层传递用Context - 避免prop drilling的利器
5️⃣ 全局状态选Redux - 复杂应用的终极解决方案

组件通信如同精密的神经网络,决定了应用的响应能力和可维护性。当你在下次项目中遇到组件"失联"困境时,不妨回到这里寻找解决方案。愿你在React的宇宙中,构建出组件间完美协作的星辰大海!