管理依赖状态:异步资源状态及Action处理技术咨询
Hey, let's break down how to handle this dependent state scenario (your code looks like Redux, so I'll frame solutions around that ecosystem). There are a few solid approaches depending on whether your programmeColors calculation is synchronous or requires additional async work:
programmeColors Directly in the Reducer Since programmeColors is purely dependent on programmeCodes, you can calculate it directly in your reducer when the programmeCodes are fetched. This keeps your state in sync automatically and maintains a single source of truth.
// Define your initial state const initialState = { programmeCodes: null, programmeColors: null, isLoading: false, error: null }; // Add the missing action type if needed export const RECEIVE_PROGRAMME_CODES = 'RECEIVE_PROGRAMME_CODES'; export const REQUEST_PROGRAMME_CODES = 'REQUEST_PROGRAMME_CODES'; export const FETCH_PROGRAMME_CODES_FAILED = 'FETCH_PROGRAMME_CODES_FAILED'; // Helper function to calculate colors from codes (keep reducer logic clean) const calculateProgrammeColors = (codes) => { // Replace with your actual color mapping logic return codes.reduce((colorMap, code) => { colorMap[code] = getColorForCode(code); // e.g., a function that maps code to hex/rgb return colorMap; }, {}); }; function programmeReducer(state = initialState, action) { switch (action.type) { case REQUEST_PROGRAMME_CODES: return { ...state, isLoading: true, error: null }; case RECEIVE_PROGRAMME_CODES: const programmeColors = calculateProgrammeColors(action.payload); return { ...state, isLoading: false, programmeCodes: action.payload, programmeColors }; case FETCH_PROGRAMME_CODES_FAILED: return { ...state, isLoading: false, error: action.payload }; default: return state; } }
Why this works: Your programmeColors will always mirror the latest programmeCodes without extra state management overhead. No risk of the two states getting out of sync.
If calculating programmeColors is computationally heavy, or you need to reuse this logic across multiple components, use a memoized selector (via the reselect library). This avoids recalculating colors unless programmeCodes actually changes.
import { createSelector } from 'reselect'; // Base selector to get programmeCodes from state const selectProgrammeCodes = (state) => state.programmes.programmeCodes; // Memoized selector to derive programmeColors export const selectProgrammeColors = createSelector( [selectProgrammeCodes], (programmeCodes) => { if (!programmeCodes) return null; // Same color calculation logic as before return programmeCodes.reduce((colorMap, code) => { colorMap[code] = getColorForCode(code); return colorMap; }, {}); } );
Why this works: You don't need to store programmeColors in your Redux store at all—you compute it on-demand when components need it. The memoization ensures the calculation only runs when programmeCodes updates, which is great for performance.
If programmeColors requires a separate API call (e.g., fetching color data based on the retrieved codes), extend your async action creator to chain the two requests.
export const RECEIVE_PROGRAMME_COLORS = 'RECEIVE_PROGRAMME_COLORS'; function receiveProgrammeColors(colors) { return { type: RECEIVE_PROGRAMME_COLORS, payload: colors }; } export function fetchProgrammeCodes() { return async (dispatch) => { dispatch(requestProgrammeCodes()); try { // First fetch the codes const programmeCodes = await api.fetchProgrammeCodes(); dispatch(receiveProgrammeCodes(programmeCodes)); // Then fetch colors using the codes const programmeColors = await api.fetchColorsForCodes(programmeCodes); dispatch(receiveProgrammeColors(programmeColors)); } catch (error) { dispatch({ type: FETCH_PROGRAMME_CODES_FAILED, payload: error.message }); } }; } // Update reducer to handle the new action function programmeReducer(state = initialState, action) { switch (action.type) { // ... existing cases case RECEIVE_PROGRAMME_COLORS: return { ...state, programmeColors: action.payload }; default: return state; } }
Why this works: This handles sequential async operations where the second request depends on the first. Just make sure to add error handling for both steps to avoid stuck loading states.
Quick Recommendation
- For synchronous color calculations: Use memoized selectors (cleanest, keeps store lean) or reducer-derived state.
- For async color data: Use chained async actions.
内容的提问来源于stack exchange,提问作者Tomasz Szawara

