一、学习目标
- 掌握 React 状态管理方案: useState、useReducer、useContext、useSyncExternalStore 等常用钩子
- 掌握常见的集中状态管理库: 熟悉 Redux、Zustand、Jotai 等常见的 React 集中状态管理方案
- 了解 Redux 原理: 理解 Redux 的核心概念,包括单一状态树、reducer、action 和 state flow,掌握其单向数据流的实现方式
- 手写 Redux: 从零开始手写简化版 Redux,实现核心功能,如 createStore、combineReducers、applyMiddleware 等,理解底层实现原理
二、补充资料
- 官方文档:https://react.dev/reference/react/useReducer
- useState 与 useReducer 的同一出处:https://github.com/facebook/react/blob/454fb35065053d5572689af1a7c125a41849604a/packages/react-reconciler/src/ReactFiberHooks.js#L1883
- useTransition 的定义:https://github.com/facebook/react/blob/454fb35065053d5572689af1a7c125a41849604a/packages/react-reconciler/src/ReactFiberHooks.js#L3245
- 基于 proxy 实现的状态管理:https://valtio.pmnd.rs/
- 原子状态 jotai:https://jotai.org/
- 新晋状态管理 zustand:https://docs.pmnd.rs/zustand/getting-started/introduction
三、面试真题
用过哪些状态管理方案,怎么选择?
说说你对 Redux 的理解?
状态改变引发视图频繁更新,怎么优化?
四、基础状态管理方案
状态方案选型依据
- 基础简单场景,组件状态管理,useState
- 状态相对复杂,但是不需要全局存储的话,复杂组件状态管理,useReducer
- 状态跨层级消费,换肤、国际化,垮层级状态管理useContext
- 状态在跨组件间使用,并且相对复杂,我们就考虑使用集中状态管理方案(redux、zustand、jotai)
1、useState
import { useEffect, useState } from 'react';
interface InputProps<T> {
value: T;
onChange: (value: T) => void;
}
function Input<T extends string | number | readonly string[]>({ value, onChange }: InputProps<T>) {
const [innerValue, setInnerValue] = useState(value);
useEffect(() => {
onChange(innerValue);
}, [innerValue]);
return (
<div>
<input value={innerValue} onChange={e => setInnerValue(e.target.value as T)} />
</div>
);
}
const Text = () => {
const [value, setValue] = useState<string>('');
return (
<div>
<Input value={value} onChange={setValue} />
<div>{value}</div>
</div>
);
};
export default Text;
2、useReducer
CardInfo demo
版本一
const CardInfo = () => {
const [title, setTitle] = useState('');
const [body, setBody] = useState('');
const [footer, setFooter] = useState('');
return (
<div>
<div>
card title --- {title}
<input value={title} onChange={e => setTitle(e.target.value)} />
</div>
<div>
card body --- {body}
<input value={body} onChange={e => setBody(e.target.value)} />
</div>
<div>
card footer --- {footer}
<input value={footer} onChange={e => setFooter(e.target.value)} />
</div>
</div>
)
}
export default CardInfo;
版本二优化
const CardInfo = () => {
const [state, setState] = useState({
title: '',
body: '',
footer: '',
})
return (
<div>
<div>
card title --- {state.title}
<input value={state.title} onChange={e => setState({ ...state, title: e.target.value })} />
</div>
<div>
card body --- {state.body}
<input value={state.body} onChange={e => setState({ ...state, body: e.target.value })} />
</div>
<div>
card footer --- {state.footer}
<input value={state.footer} onChange={e => setState({ ...state, footer: e.target.value })} />
</div>
</div>
)
}
export default CardInfo;
版本三 useReducer
const initialState = {
title: '',
body: '',
footer: '',
}
//策略模式管理状态
function reducer(
state: {
title: string, body: string, footer: string
},
action: {
type: 'modifyTitle' | 'modifyBody' | 'modifyFooter';
payload: Partial<{
title: string,
body: string,
footer: string,
}>
}) {
switch (action.type) {
case "modifyTitle":
return { ...state, title: action.payload.title || state.title };
case "modifyBody":
return { ...state, body: action.payload.body || state.body };
case "modifyFooter":
return { ...state, footer: action.payload.footer || state.footer };
default:
break;
}
}
const CardInfo = () => {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<div>
<div>
card title --- {state.title}
<input
value={state.title}
onChange={e => dispatch({ type: 'modifyTitle', payload: { title: e.target.value } })}
/>
</div>
<div>
card body --- {state.body}
<input
value={state.body}
onChange={e => dispatch({ type: 'modifyBody', payload: { body: e.target.value } })}
/>
</div>
<div>
card footer --- {state.footer}
<input
value={state.footer}
onChange={e => dispatch({ type: 'modifyFooter', payload: { footer: e.target.value } })}
/>
</div>
</div>
)
}
export default CardInfo;
+ - demo
const initialState = { count: 0 };
function reducer(
state: typeof initialState,
action: {
type: 'increment' | 'decrement' | 'addto100';
payload?: { count: number }
}) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
case "addto100":
return { count: action.payload?.count || state.count };
default:
throw new Error();
}
}
const Text = () => {
const [state, dispatch] = useReducer(reducer, initialState)
const handleIncrease = () => {
dispatch({ type: 'increment' })
}
const handleDecrease = () => {
dispatch({ type: 'decrement' })
}
const handleGoTo100 = () => {
dispatch({ type: 'addto100', payload: { count: 100 } })
}
return (
<div>
<h1>Count{state.count}</h1>
<button onClick={handleIncrease}>+</button>
<button onClick={handleDecrease}>-</button>
<button onClick={handleGoTo100}>100</button>
</div>
)
}
export default Text;
3、useContext
传递样式 demo
import { createContext, useContext } from 'react';
const ThemeContext = createContext<string>('blue')
const ThemeProvider = () => {
const [theme, setTheme] = useState<string>("blue")
return (
<ThemeContext.Provider value={theme}>
<ThemeComponent/>
<button
onClick={() => setTheme(theme === "blue" ? "red": "blue")}>
toggle
</button>
</ThemeContext.Provider>
)
}
const ThemeComponent = () => {
const theme = useContext(ThemeContext)
return (
<div>
<div style={{color: theme}}>useContext</div>
</div>
)
}
export default ThemeProvider
国际化 demo
import { createContext } from 'react';
interface I18nContextValue {
locale: 'zn' | 'en' | 'jp';
}
const I18nContext = createContext<I18nContextValue | null>(null)
4、zustand
下载安装依赖
npm install zustand
创建一个Store src/stores/useCount.ts
import { create } from 'zustand';
type counterState = {
count: number;
increment: () => void;
decrement: () => void;
double: () => void;
}
const useStore = create<counterState>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
double: ()=> set((state) => ({ count: state.count * 2 })),
}));
export default useStore;
使用这个useCount.ts
import useCount from '@/stores/useCount';
const Counter = () => {
const { count, increment, decrement } = useCount();
return (
<div>
<h1>{count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
export default Counter;
zustand组件拆分,这样每个数据变化不会引起其他没有变化的数据重新渲染
Store src/stores/useCount.ts
import { create } from 'zustand';
type counterState = {
count: number;
name: string;
increment: () => void;
decrement: () => void;
double: () => void;
changeName: () => void;
}
const useStore = create<counterState>((set) => ({
count: 0,
name: 'pig',
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
double: ()=> set((state) => ({ count: state.count * 2 })),
changeName: () => set((state) => ({ name: state.name + '1' })),
}));
export default useStore;
useCount.ts
import useCount from '@/stores/useCount';
const Count = () => {
//细颗粒度订阅
const count = useCount((state) => state.count);
useEffect(() => {
console.log(count);
});
return (
<h1>{count}</h1>
)
}
const Name = () => {
const name = useCount((state) => state.name);
useEffect(() => {
console.log(name);
});
return (
<h1>{name}</h1>
)
}
const Counter = () => {
const increment = useCount((state) => state.increment);
const decrement = useCount((state) => state.decrement);
const double = useCount((state) => state.double);
const changeName = useCount((state) => state.changeName);
return (
<div>
<Count/>
<Name/>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={double}>double</button>
<button onClick={changeName}>changeName</button>
</div>
);
};
export default Counter;
5、useSyncExternalStore
控制外部状态改变,ui更新
状态库核心实现
6、jotai(这个如果后期有项目用再学,没有就不学了)
五、Redux 原理深度剖析
实现一个简单的redux
2388

被折叠的 条评论
为什么被折叠?



