状态管理
Vuex
User Dispatch => action => mutation => state
Javascript
// 组件调用
this.$dispatch({ type: 'getCount', payload: 'xx' });
// store
const store = {
state: () => ({
count: 1,
}),
mutations: {
// 同步方法
increment(state, num) {
state.count = num;
},
},
actions: {
// 异步方法
async getCount({ dispatch, state, commit, rootState }, { type, payload }) {
const num = await Promise.resolve(2);
commit('increment', num);
},
},
};
React
Flux 模型
redux
同步 reducer
Javascript
// @ts-nocheck
// 实现
const createStore = (reducer, initState, middlewareFn) => {
if (middlewareFn) {
return middlewareFn(createStore)(reducer, initState);
}
let state = initState,
listeners = [];
const getState = () => state;
const dispatch = action => {
state = reducer(state, action);
listeners.forEach(listener => listener(state));
};
const subscribe = listener => {
listeners.push(listener);
return () => (listeners = listeners.filter(l => l !== listener));
};
return {
getState,
subscribe,
dispatch,
};
};
const reducer = (state, { type, payload }) => {
switch (type) {
case 'x':
return state + payload;
}
};
Middleware 中间件(只能是同步方法)
Javascript
//
function applyMiddleware(...middlewares: Middleware[]): StoreEnhancer<any> {
return createStore => (reducer, preloadedState) => {
const store = createStore(reducer, preloadedState);
let dispatch: Dispatch = () => {
throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.');
};
const middlewareAPI: MiddlewareAPI = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args),
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose<typeof dispatch>(...chain)(store.dispatch);
return {
...store,
dispatch,
};
};
}
// 使用方式
const store = createStore(reducer, initial_state, applyMiddleware(logger));
异步操作
异步操作 可拆分成 3 个同步操作
- 发起时
- 成功时
- 失败时
需要在异步方法里面添加 3 个操作,则 disatch 只能是个 function Redux-thunk Dispatch 的内容不再是 对象,而是一个 function https://github1s.com/reduxjs/redux-thunk/blob/HEAD/src/index.ts
Javascript
function createThunkMiddleware<
State = any,
BasicAction extends Action = AnyAction,
ExtraThunkArg = undefined
>(extraArgument?: ExtraThunkArg) {
// Standard Redux middleware definition pattern:
// See: https://redux.js.org/tutorials/fundamentals/part-4-store#writing-custom-middleware
const middleware: ThunkMiddleware<State, BasicAction, ExtraThunkArg> =
({ dispatch, getState }) =>
next =>
action => {
// The thunk middleware looks for any functions that were passed to `store.dispatch`.
// If this "action" is really a function, call it and return the result.
if (typeof action === 'function') {
// Inject the store's `dispatch` and `getState` methods, as well as any "extra arg"
return action(dispatch, getState, extraArgument)
}
// Otherwise, pass the action down the middleware chain as usual
return next(action)
}
return middleware
}
另外一种形式可以返回 promise 对象,需要配合 redux-actions 使用 Redux-promise https://github1s.com/redux-utilities/redux-promise/blob/HEAD/src/index.js
Javascript
export default function promiseMiddleware({ dispatch }) {
return next => action => {
if (!isFSA(action)) {
return isPromise(action) ? action.then(dispatch) : next(action);
}
return isPromise(action.payload)
? action.payload
.then(result => dispatch({ ...action, payload: result }))
.catch(error => {
dispatch({ ...action, payload: error, error: true });
return Promise.reject(error);
})
: next(action);
};
}
Redux-action 主要是统一了一下格式配置 redux-promise https://github1s.com/redux-utilities/redux-actions/blob/HEAD/src/createAction.js#L27-L41
Javascript
const action = { type };
if (payload instanceof Error) {
action.error = true;
}
if (payload !== undefined) {
action.payload = payload;
}
if (hasMeta) {
action.meta = metaCreator(...args);
}
React-redux
https://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html
对组件做了区分
- 容器组件 (只包含 state)
- UI 组件 (不包含 state)
recoil
mobx
参考: Redux 教程,阮一峰 https://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html
简易实现
Local State in React (Component-Level State)
React’s built-in state management is primarily handled through the useState
and useReducer
hooks.
useState:
Manages local state in functional components. It provides a way to declare and update state within a component.
Simple for managing individual pieces of state (e.g., form inputs, counters).
Example:
const [count, setCount] = useState(0); return ( <div> <p>{count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> );
useReducer:
A more powerful alternative to
useState
, particularly useful when the state logic becomes complex or when state updates depend on the previous state.Example:
const initialState = { count: 0 }; function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: return state; } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> </> ); }
Global State in React
As applications grow, components often need to share state. Relying solely on props
for this purpose can lead to "prop drilling," where state is passed down multiple component layers. To handle shared or global state more efficiently, React provides tools like Context
and external state management libraries.
1. React Context API
The Context API allows you to create global state accessible to any component without explicitly passing props.
Best suited for relatively simple global state management (e.g., theming, user authentication).
Example:
const ThemeContext = React.createContext(); function App() { const [theme, setTheme] = useState('dark'); return ( <ThemeContext.Provider value={theme}> <Toolbar /> </ThemeContext.Provider> ); } function Toolbar() { return <ThemedButton />; } function ThemedButton() { const theme = useContext(ThemeContext); return <button className={theme}>Button</button>; }
Limitations:
- Overuse of context for frequent or large-scale state updates can lead to performance issues, as any component consuming the context will re-render when the context value changes.
2. Third-Party State Management Libraries
For more complex state management across larger applications, external libraries are often used. These libraries provide advanced state synchronization, performance optimizations, and better dev tooling.
Redux:
Redux is a predictable state container, often used in large-scale applications.
It follows a unidirectional data flow pattern with actions, reducers, and a central store.
Benefits: Provides strict control over state updates, time-travel debugging, middleware for handling side effects.
Example:
const increment = () => ({ type: 'INCREMENT' }); function reducer(state = { count: 0 }, action) { switch (action.type) { case 'INCREMENT': return { count: state.count + 1 }; default: return state; } } const store = createStore(reducer); function App() { const count = useSelector(state => state.count); const dispatch = useDispatch(); return ( <> <p>{count}</p> <button onClick={() => dispatch(increment())}>Increment</button> </> ); }
Recoil:
Recoil provides a simple but powerful state management library optimized for React.
It allows you to manage both local and global state with atoms and selectors.
Example:
const textState = atom({ key: 'textState', // unique ID (with respect to other atoms/selectors) default: '', // default value (aka initial value) }); function TextInput() { const [text, setText] = useRecoilState(textState); return <input type="text" value={text} onChange={e => setText(e.target.value)} />; }
MobX:
- MobX is a library that uses observable objects for reactive state management, providing an alternative to Redux’s strict state immutability approach.
- It automatically tracks dependencies and only updates components when necessary, leading to optimized re-rendering.
Zustand:
A lightweight and simpler alternative to Redux. It provides hooks for managing state in an intuitive and minimalistic way.
Example:
const useStore = create(set => ({ count: 0, increment: () => set(state => ({ count: state.count + 1 })), })); function Counter() { const count = useStore(state => state.count); const increment = useStore(state => state.increment); return ( <> <p>{count}</p> <button onClick={increment}>Increment</button> </> ); }
实现一个简易 store
details
import { useEffect, useState } from 'react';
export function createStore(initialState) {
let state = initialState;
let listeners: any[] = [];
const getState = () => state;
const setState = newState => {
state = typeof newState === 'function' ? newState(state) : newState;
listeners.forEach(listener => listener(state));
};
const subscribe = fn => {
listeners.push(fn);
return () => {
listeners = listeners.filter(l => l !== fn);
};
};
return {
getState,
setState,
subscribe,
};
}
export function useStore(store, selector = state => state) {
const [selectedState, setSelectedState] = useState(selector(store.getState()));
useEffect(() => {
const unsubscribe = store.subscribe(newState => {
const newSelectedState = selector(newState);
setSelectedState(newSelectedState);
});
return unsubscribe;
}, [store, selector]);
return selectedState;
}