父子组件
原理:
- 父传子:父组件直接在子组件标签上传递属性,子组件使用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方案:
- 创建上下文对象、在顶层组件
- 通过Provider提供数据、在底层组件
- 通过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
)。