You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Redux技术问题:如何基于已有状态创建新状态?

Deriving New State from Existing State in Redux (Names/Hobbies Example)

Awesome that you’ve nailed the core Redux building blocks—action creators, actions, reducers, dispatchers! Let’s break down how to derive new state from existing state using your names/hobbies scenario. There are two common approaches here: calculating derived state directly in your reducer, or using selectors to compute it on-demand (the latter is usually preferred to avoid redundant state in your store).

1. Calculating Derived State in Your Reducer

If you need to persist the derived state in your store (rare, but useful for specific cases), you can compute it directly in your reducer by accessing other parts of the current state.

First, let’s formalize your existing setup as you described:

// actionTypes.js
export const NAMES = 'NAMES';
export const HOBBIES = 'HOBBIES';

// actionCreators.js
export const addName = (name) => ({
  type: NAMES,
  payload: name
});

export const addHobby = (hobby) => ({
  type: HOBBIES,
  payload: hobby
});

Now, in your reducer, let’s say we want to maintain a combinedItems state that merges both names and hobbies. We’ll update this every time a name or hobby is added, using the existing state values:

// reducer.js
import { NAMES, HOBBIES } from './actionTypes';

const initialState = {
  names: [],
  hobbies: [],
  combinedItems: [] // Our derived state
};

const rootReducer = (state = initialState, action) => {
  switch (action.type) {
    case NAMES:
      const updatedNames = [...state.names, action.payload];
      // Derive new combinedItems using updated names + existing hobbies
      const newCombinedItems = [...updatedNames, ...state.hobbies];
      return {
        ...state,
        names: updatedNames,
        combinedItems: newCombinedItems
      };
    case HOBBIES:
      const updatedHobbies = [...state.hobbies, action.payload];
      // Derive new combinedItems using existing names + updated hobbies
      const newCombinedItems2 = [...state.names, ...updatedHobbies];
      return {
        ...state,
        hobbies: updatedHobbies,
        combinedItems: newCombinedItems2
      };
    default:
      // Initialize combinedItems on first load
      return {
        ...state,
        combinedItems: [...state.names, ...state.hobbies]
      };
  }
};

export default rootReducer;

In your App.js, you can dispatch actions as usual, and the derived combinedItems state will update automatically:

// App.js
import { useDispatch, useSelector } from 'react-redux';
import { addName, addHobby } from './actionCreators';

function App() {
  const dispatch = useDispatch();
  const combinedItems = useSelector(state => state.combinedItems);

  return (
    <div>
      <button onClick={() => dispatch(addName('Alice'))}>Add Name</button>
      <button onClick={() => dispatch(addHobby('Reading'))}>Add Hobby</button>
      <h3>Combined Items:</h3>
      <ul>
        {combinedItems.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

2. Using Selectors for On-Demand Derived State

Most of the time, you don’t need to store derived state in your store—you can compute it when you need it using selectors. Selectors are pure functions that take the full Redux state and return the derived data. This keeps your store lean and avoids redundant state updates.

Let’s create a selector that combines names and hobbies, or a more complex one that associates names with their hobbies (assuming your data has user IDs):

// selectors.js
// Simple selector to combine names and hobbies
export const getCombinedItems = (state) => {
  return [...state.names, ...state.hobbies];
};

// Complex selector: Associate each name with their hobbies (structured data example)
export const getUserHobbies = (state) => {
  return state.names.map(name => ({
    ...name,
    hobbies: state.hobbies.filter(hobby => hobby.userId === name.id)
  }));
};

Now, use this selector in your component to get the derived state without modifying your reducer:

// App.js
import { useDispatch, useSelector } from 'react-redux';
import { addName, addHobby } from './actionCreators';
import { getUserHobbies } from './selectors';

function App() {
  const dispatch = useDispatch();
  const userHobbies = useSelector(getUserHobbies);

  return (
    <div>
      <button onClick={() => dispatch(addName({ id: 1, name: 'Alice' }))}>Add Alice</button>
      <button onClick={() => dispatch(addHobby({ id: 1, name: 'Reading', userId: 1 }))}>Add Reading</button>
      <h3>User Hobbies:</h3>
      <ul>
        {userHobbies.map(user => (
          <li key={user.id}>
            {user.name}: {user.hobbies.map(h => h.name).join(', ')}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;

Quick Tips

  • For performance, use memoized selectors (via libraries like reselect) to avoid recalculating derived state when unrelated parts of the store change.
  • Sticking with selectors keeps your store focused on raw source data, ensuring a single source of truth for your app’s state.

内容的提问来源于stack exchange,提问作者Peter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:05:52