React组件通信

父子组件

原理

  • 父传子:父组件直接在子组件标签上传递属性,子组件使用props来接收
  • 子传父:父组件在子组件标签上传递方法,子组件通过props接收后,调用父组件的方法并传参数

实现

jsx 复制代码
import { useState, useCallback,useCallback } from 'react'
function Child({count,changeCount}){
    const handleClick = () => {
        changeCount(20)
    }
    return (
        <>
            <div> count: {count} </div>
            <button onClick={handleClick}>改变count值</button>
        </>        
    )
}

function Parent(){
    const [count, setCount] = useState(0)
    const changeCount = (payload) => {
        setCount(preCount => preCount + payload)
    }
    renturn <Child count={count} changeCount={changeCount} />
}

跨组件层级

  • Context方案:
    1. 创建上下文对象、在顶层组件
    2. 通过Provider提供数据、在底层组件
    3. 通过useContext来使用数据
jsx 复制代码
import { createContext, useContext} from 'react';
//1、创建上下文对象
const MsgContext = createContext();

//2、在顶层组件,通过Provider提供数据
function App(){
    const msg = 'this is appMsg'
    return (
        <div>
            <MsgContext.Provider value={msg}>
                this is App component
            	<A></A>
            </MsgContext.Provider>
        </div>
    )
}


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

//3、在底层组件,通过useContext来使用数据
function B(){
    const msg = useContext(MsgContext)
    return (
        <div>this is B component,{msg}</div>
    )
}

全局状态管理方案

1. redux

  • 安装:npm i @reduxjs/toolkit
  • 目录层级
text 复制代码
-src
    -store // store文件夹
        -modules // store的子模块文件夹
            -countStore.js // countStore文件
        -index.js // store入口文科
    index.js // 应用入口文件
  • store代码实现
js 复制代码
// src/store/module/countStore.js文件

import {createSlice} from '@reduxjs/toolkit'
const store = createSlice({
   name:'aStore',
   initialState:{
       count:0
   },
   reducers:{
       add(state,params){
           if(state.count + params.payload > 100){
               return
           }
           state.count += params.payload
       },
       sub(state,params){
           if(state.count - params.payload < 0){
               return
           }
           state.count -= params.payload
       },        
   }
})

// 异步操作
const addAsync = () => {
   return async (dispatch) => {
       const res = await fetch('/api/getData');
       const data = await res.json()
       dispatch(add(data))
   }
}

export const {add,sub} = store.actions
export default store.reducer
js 复制代码
// src/store/index.js 文件
import { configureStore } from "@reduxjs/toolkit";
import countStore from './modules/countStore'
const store = configureStore({
    reducer:{
        countStore,                
    }
})
export default store
js 复制代码
// src/index.js 文件

import { RouterProvider } from 'react-router';
import { Provider } from 'react-redux';
import store from '@/store';
import routers from '@/router/index'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <RouterProvider router={routers} />
    </Provider>
  </React.StrictMode>
);
  • 组件内使用store
jsx 复制代码
import { useSelector, useDispatch } from "react-redux";
import { add, sub } from "@/store/modules/countStore";

function Home(){
    const { count } = useSelector((state)=>{
        console.log('state',state);

        return state.countStore
   })
   const dispatch = useDispatch()
   
   return (
        <button onClick={()=>dispatch(sub(9))}>-9</button>
        <span>count value: {count}</span>
        <button onClick={()=>dispatch(add(20))}>+20</button>
   )
}

总结:大型项目,需要复杂状态管理的项目

3. Zustand

  • 安装:npm i zustand
  • 实现store
jsx 复制代码
export const useUserStore = create((set) => ({
  user: null,
  fetchUser: async (userId) => {
    const response = await fetch(`/api/users/${userId}`);
    const user = await response.json();
    set({ user }); // 异步完成后更新状态
  },
}));

// 组件中使用
const UserProfile = ({ userId }) => {
  const { user, fetchUser } = useUserStore();
  useEffect(() => {
    fetchUser(userId);
  }, [userId]);
  return <div>{user?.name}</div>;
};
  • 持久化存储
js 复制代码
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

// 使用 persist 中间件,指定存储位置和序列化方式
export const useAuthStore = create()(
  persist(
    (set) => ({
      token: null,
      setToken: (token) => set({ token }),
      clearToken: () => set({ token: null }),
    }),
    {
      name: 'auth-storage', // localStorage 键名
      storage: {
        getItem: (name) => localStorage.getItem(name), // 自定义存储(可选)
        setItem: (name, value) => localStorage.setItem(name, value),
        removeItem: (name) => localStorage.removeItem(name),
      },
      // 仅持久化部分状态(可选)
      partialize: (state) => ({ token: state.token }), 
    }
  )
);
  • 模块化store:对于大型应用,可将状态拆分为多个独立的 Store,避免单一文件过大。
js 复制代码
// store/cartStore.ts(购物车状态)
export const useCartStore = create((set) => ({ /* ... */ }));

// store/themeStore.ts(主题状态)
export const useThemeStore = create((set) => ({ /* ... */ }));

总结:zustand轻量,不需要写太多样板代码,适合中小型项目

4. Jotai

  • 安装:npm i jotai
  • 实现
jsx 复制代码
// atoms/user.ts
import { atom } from 'jotai';
import { fetchUser } from '../api'; // 假设这是一个返回 Promise 的 API 函数

// 定义用户原子,存储数据、加载状态、错误信息
export const userAtom = atom({
  data: null,
  isLoading: false,
  error: null,
});

// 异步更新函数:获取用户数据
export const fetchUserAtom = atom(
  null, // 无初始值(仅作为操作触发)
  async (get, set, userId: string) => { // updateFn(支持 async)
    set(userAtom, (prev) => ({ ...prev, isLoading: true, error: null })); // 开始加载
    try {
      const data = await fetchUser(userId); // 等待 API 请求完成
      set(userAtom, (prev) => ({ ...prev, data, isLoading: false })); // 成功:更新数据
    } catch (err) {
      set(userAtom, (prev) => ({
        ...prev,
        error: err instanceof Error ? err : new Error('Failed to fetch user'),
        isLoading: false,
      })); // 失败:记录错误
    }
  }
);

总结:原子化、轻量、与 React 响应式深度集成,适合细粒度异步状态管理

Redux、Zustand、Jotai如何选

  • ​选 Jotai​:如果项目较小,需要细粒度状态共享(如组件间少量数据同步),或追求简洁的 API 和低学习成本。
  • ​选 Zustand​:如果项目中等规模,需要全局状态管理但不想被 Redux 的模板限制,或需要灵活扩展(如持久化、日志)。
  • ​选 Redux​ :如果项目复杂(如大型后台、电商),需要严格的状态可预测性、跨团队协作,或依赖高级异步处理(如 Redux Saga)。
相关推荐
gAlAxy...16 分钟前
深入理解 Cookie 与 Session —— Web 状态保持详解与实战
前端
专注VB编程开发20年23 分钟前
c#,vb.net全局多线程锁,可以在任意模块或类中使用,但尽量用多个锁提高效率
java·前端·数据库·c#·.net
JarvanMo27 分钟前
Google Connect 8月14日纪实
前端
猩猩程序员1 小时前
Go 1.24 全面拥抱 Swiss Table:让内置 map 提速 60% 的秘密
前端
1024小神1 小时前
vue3 + vite项目,如果在build的时候对代码加密混淆
前端·javascript
轻语呢喃1 小时前
useRef :掌握 DOM 访问与持久化状态的利器
前端·javascript·react.js
wwy_frontend2 小时前
useState 的 9个常见坑与最佳实践
前端·react.js
w_y_fan2 小时前
flutter_riverpod: ^2.6.1 应用笔记 (一)
前端·flutter
Jerry2 小时前
Compose 界面工具包
前端
Focusbe2 小时前
从0到1开发一个AI助手
前端·人工智能·面试