如何在React-Redux简易Reducer中更好处理错误与状态更新
Hey there, great question—this is a super common gotcha when learning Redux, so let's unpack this step by step.
First, let's recap why your initial test showed no UI updates: Redux (and React-Redux) relies on reference changes to detect state updates. If you return the exact same state object reference (instead of a new one), the connected components won't pick up on the change, so render() never fires. That's why the spread operator (...) is critical—it lets you create a new object that copies all existing state properties, then you can override the ones you need to update.
Now, onto handling error states: errors should be part of your reducer's state shape from the start. Here's how to do it properly:
1. Define an initial state with an error field
First, structure your state to include a dedicated spot for error information. For example:
const initialState = { data: [], loading: false, error: null // Initialize error as null when there's no error };
2. Update the error state immutably
When an error action is dispatched (like a failed API call), return a new state object using the spread operator to preserve existing state, then set the error field. Here's how that looks in a reducer:
const myReducer = (state = initialState, action) => { switch (action.type) { case 'FETCH_DATA_SUCCESS': return { ...state, loading: false, data: action.payload, error: null // Clear error on success }; case 'FETCH_DATA_FAILURE': return { ...state, loading: false, error: action.payload // Set error message/object }; case 'FETCH_DATA_REQUEST': return { ...state, loading: true, error: null // Reset error when starting a new request }; default: return state; } };
3. Simplify with Immer (optional but recommended)
If your state has nested objects, manually spreading every level can get tedious. Tools like Immer let you write "mutating" code that gets converted into immutable updates under the hood. Here's the same error handling with Immer:
import { produce } from 'immer'; const myReducer = produce((draft, action) => { switch (action.type) { case 'FETCH_DATA_FAILURE': draft.loading = false; draft.error = action.payload; break; case 'FETCH_DATA_SUCCESS': draft.loading = false; draft.data = action.payload; draft.error = null; break; case 'FETCH_DATA_REQUEST': draft.loading = true; draft.error = null; break; } }, initialState);
Immer takes care of creating new state references for you, so you don't have to worry about missing a spread operator.
Key Takeaways
- Never modify the existing
stateobject directly (e.g.,state.error = action.payload) and return it—this keeps the same reference, so UI won't update. - Always return a new state object when updating, using the spread operator or tools like Immer to preserve existing state while changing what's needed.
- Error states are just part of your normal state shape—treat them like any other piece of state that needs immutable updates.
内容的提问来源于stack exchange,提问作者Peter Kellner

