Best Practices
- 区分 Smart Component (know the state) 和 Dump Component (stateless)
- Component 里不要出现任何 async calls,交给 action creator 来做
- Reducer 尽量简单,复杂的交给 action creator
- Reducer 里 return 新 state 的时候:
- Redux Devtools
- Redux React Style Guide
- Simple Redux API
// add new item to state array
// bad and does not work case "ADD":
state.push(newItem)
// Good case "ADD":
return [...state, newItem]
// delete new item to state array
// bad and does not work case "DELETE":
state.splice(index, 1)
// Good case "DELETE":
state.slice(0, index).concat(state.slice(index + 1))
// update new item to state array
// First way case "EDIT":
state
.slice(0, index)
.concat([{ id: 'id', value: 'newValue' }])
.slice(index + 1)
// Second way case "EDIT":
state.map((item) => {
if (item.id === 'id') {
return {
...item,
value: 'newValue',
}
} else {
return item
}
})
- Action creator: 用 promise/async/await 以及 redux-thunk 实现异步操作.
// bad
function loadTodo(id) {
return async (dispatch, getState) => {
// only fetch the todo if it isn't already loaded
if (!getState().todos.includes(id)) {
const todo = await fetch(`/todos/${id}`)
dispatch(addTodo(todo))
}
}
}
// good
function loadTodo(id, todos) {
return async (dispatch) => {
// only fetch the todo if it isn't already loaded
if (!todos.includes(id)) {
const todo = await fetch(`/todos/${id}`)
dispatch(addTodo(todo))
}
}
}
const fluxStandardAction = {
type: 'ADD_TODO',
payload: {
text: 'Do something',
},
meta,
}
const fluxStandardAction = {
type: 'ADD_TODO',
payload: new Error('Error'),
error: true,
}
Necessity
Necessity for importing Redux (状态多, 变化快, 更新复杂):
- Lots of state.
- Frequent update state.
- Complex update state.
Style Guide
Redux style guide:
- Only one store per app.
- Avoid mutate state without ImmerJS.
- Avoid side effects in reducers.
- Avoid non-serializable values in state store.
- Normalize complex nested/relational state.
- Keep state minimal and derive additional values.
- Split large data selection into multiple small
useSelector.
States
- Remote state: Anything coming from backend, API, database, etc., could be handled by data-fetching library like TanStack Query or SWR.
- Query params in URL state: If your router doesn't support syncing those with local state, use nuqs and save yourself massive pain implementing that sync manually.
- Local state:
作为组件局部状态管理器来用.
对于只影响单个组件实例的状态,
应作为 Local State 交由
useState/useReducer管理, 而不是将其并入 Global Store. - Complex shared state: Split state into different atoms with Zustand or JoTai, atoms can be imported for any specific component without single-entry point, each atom handling different app domain/context (reducer).
- Complex UI change: 用 component 归一化处理.
- Complex data input: 用 RxJS/observable 归一化处理.
- Complex state change: 用 action/state 归一化处理.