React组件通信与Redux状态管理完全指南

引言

作为前端开发者,我们经常需要在不同组件之间传递数据和状态。React提供了多种组件通信方式,从简单的props传递到复杂的状态管理库,每种方式都有其适用场景。今天我们就来全面探讨React中的组件通信方法和Redux状态管理,帮助你在不同场景下选择最合适的通信方式。

一、父子组件通信

父子组件通信是React中最基本、最常用的通信方式,就像父母和孩子说话一样直接。

在React中,父组件可以通过props向子组件传递数据。props是React组件的一个重要概念,它允许我们在组件之间传递任意类型的数据,包括数字、字符串、对象、函数等。

jsx 复制代码
// 父组件
import { useState } from 'react';
import Child from './Child';

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

  return (
    <div>
      <Child list={list} />
    </div>
  );
}

// 子组件
export default function Child({ list }) {
  return (
    <div>
      <ul>
        {list.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

在上面的例子中,父组件通过props将list数据传递给子组件,子组件接收后渲染出来。这种方式简单直接,适用于父组件向子组件传递数据的场景。

二、子父组件通信

如果孩子想和父母说话,就需要父母给孩子一个"传话筒"。在React中,这个"传话筒"就是回调函数。

父组件可以将一个函数作为props传递给子组件,子组件调用这个函数并传递参数,从而实现子组件向父组件传递数据。

jsx 复制代码
// 父组件
import { useState } from 'react';
import Child from './Child';

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

  return (
    <div>
      <Child setList={setList} />
      <ul>
        {list.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

// 子组件
import { useRef } from 'react';

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

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

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handler}>确认</button>
    </div>
  );
}

在这个例子中,父组件将setList函数传递给子组件,子组件通过调用这个函数并传递参数,实现了向父组件传递数据的目的。这种方式适用于子组件向父组件传递数据的场景。

三、兄弟组件通信

兄弟组件之间不能直接交流,需要通过父母来传递消息。在React中,兄弟组件之间的通信通常需要通过它们的共同父组件来实现。

具体来说,我们可以让一个子组件通过回调函数将数据传递给父组件,然后父组件再通过props将数据传递给另一个子组件。

jsx 复制代码
// 父组件
import { useState } from 'react';
import Child1 from './Child1';
import Child2 from './Child2';

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

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

在这个例子中,Child1组件通过setList函数将数据传递给父组件,父组件再通过list props将数据传递给Child2组件,从而实现了兄弟组件之间的通信。

四、跨层级组件通信

当组件层级很深时,Context API就像一个"广播系统",让顶层组件能直接和深层组件通信,而不需要通过每一层组件传递props。

Context API主要由createContext、Provider和useContext三个部分组成。createContext用于创建一个上下文对象,Provider用于提供上下文数据,useContext用于消费上下文数据。

jsx 复制代码
import React, { useState, createContext, useContext } from 'react';

const myContext = createContext();

function A() {
  const { msg } = useContext(myContext);
  return (
    <div>
      <h2>我是A组件,我接受的消息是: {msg}</h2>
      <B />
    </div>
  );
}

function B() {
  const { msg } = useContext(myContext);
  return (
    <div>我是B组件, 我收到的消息是: {msg}</div>
  );
}

export default function Index() {
  const [msg, setMsg] = useState('Index 组件中的数据');
  return (
    <div>
      <myContext.Provider value={{ msg, setMsg }}>
        <A />
      </myContext.Provider>
    </div>
  );
}

在这个例子中,我们创建了一个myContext上下文对象,然后在Index组件中通过Provider提供了msg和setMsg数据,最后在A和B组件中通过useContext消费了这些数据。这种方式适用于跨层级组件之间的通信。

代码演示图:

五、Redux状态管理

对于大型应用,Redux就像一个"中央数据库",让所有组件都能访问和修改共享状态,而不需要关心组件之间的层级关系。

Redux主要由store、actions和reducers三个部分组成。store是存储状态的地方,actions是描述状态变化的对象,reducers是根据actions更新状态的函数。

jsx 复制代码
//  store/modules/counterStore.js
import { createSlice } from '@reduxjs/toolkit';

const counter = createSlice({
  name: 'counter',
  initialState: {
    count: 0,
    list: ['html', 'js', 'css'],
  },
  reducers: {
    addList(state, action) {
      state.list.push(action.payload);
    },
  },
});

const { addList } = counter.actions;
export { addList };

export default counter.reducer;

// 使用Redux的组件
import { useSelector, useDispatch } from 'react-redux';
import { addList } from '../../store/modules/counterStore';
import { useRef } from 'react';

export default function Index() {
  const { list } = useSelector((state) => state.counter);
  const inputRef = useRef(null);
  const dispatch = useDispatch();

  const handleAddList = () => {
    const inputValue = inputRef.current.value;
    dispatch(addList(inputValue));
    inputRef.current.value = '';
  };

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleAddList}>添加</button>
      <ul>
        {list.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

在这个例子中,我们使用@reduxjs/toolkit创建了一个counterStore,然后在组件中通过useSelector访问了store中的数据,通过useDispatch和addList action修改了store中的数据。这种方式适用于大型应用中的状态管理。

代码演示图:

六、Redux实战 - TodoList

让我们用Redux实现一个完整的TodoList,这是检验我们Redux掌握程度的最佳方式。

jsx 复制代码
import './todoList.css';
import { useSelector, useDispatch } from 'react-redux';
import { addList, changeDone, deleteItem } from '../../store/modules/todoListStore';
import { useRef } from 'react';

function App() {
  const { list } = useSelector((state) => state.todoList);
  const inputRef = useRef(null);
  const dispatch = useDispatch();

  const handleAddList = () => {
    const inputValue = inputRef.current.value;
    const index = list[list.length - 1]?.id || 0;
    if (inputValue === '') {
      inputRef.current.placeholder = '请输入内容(不能为空)!!!';
      setTimeout(() => {
        inputRef.current.placeholder = '';
      }, 1000);
      return;
    }
    const item = {
      id: index + 1,
      name: inputValue,
      done: false,
    };
    dispatch(addList(item));
    inputRef.current.value = '';
  };

  const handleChangeDone = (id) => (e) => {
    const checked = e.target.checked;
    dispatch(changeDone({ id, done: checked }));
  };

  const handleDelete = (id) => {
    dispatch(deleteItem(id));
  };

  return (
    <section className="todoapp">
      <header className="header">
        <h1>todos</h1>
        <input
          className="new-todo"
          autoFocus
          autoComplete="off"
          placeholder="What needs to be done?"
          onKeyDown={(e) => {
            if (e.key === 'Enter') {
              handleAddList();
            }
          }}
          ref={inputRef}
        />
      </header>
      <section className="main">
        <ul className="todo-list">
          {list.map((item) => (
            <li key={item.id} className={item.done === true ? 'todo completed' : 'todo'}>
              <div className="view">
                <input className="toggle" type="checkbox" checked={item.done} onChange={handleChangeDone(item.id)} />
                <label>{item.name}</label>
                <button className="destroy" onClick={() => handleDelete(item.id)}></button>
              </div>
            </li>
          ))}
        </ul>
      </section>
    </section>
  );
}

export default App;

在这个例子中,我们实现了一个完整的TodoList应用,包括添加、完成和删除todo项的功能。我们使用Redux来管理todo项的状态,使得组件之间的通信更加简单和清晰。

项目效果展示图:

总结

React提供了多种组件通信方式,从简单的props到复杂的Redux,每种方式都有其适用场景:

  • 父子组件通信:使用props,适用于简单的父子组件之间的数据传递。
  • 子父组件通信:使用回调函数,适用于子组件向父组件传递数据的场景。
  • 兄弟组件通信:通过共同的父组件,适用于兄弟组件之间的数据传递。
  • 跨层级组件通信:使用Context API,适用于跨层级组件之间的数据传递。
  • 大型应用状态管理:使用Redux,适用于大型应用中的状态管理。

选择合适的通信方式,可以让我们的代码更加清晰、可维护。希望这篇文章能帮助你更好地理解React组件通信和Redux状态管理,在实际开发中选择最合适的通信方式。

相关推荐
To_OC5 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
To_OC5 小时前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
vipbic5 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy5 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志6 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble7 小时前
c端系统,其实更像一个信息展示平台
前端
你怎么知道我是队长7 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
李姆斯8 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜8 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司9 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能