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

Redux Toolkit中定义依赖其他状态属性的状态的正确方式

在Redux Toolkit中处理依赖其他状态的派生属性

Redux的核心原则是只存储原始数据源,像typedChars、remainingChars这种依赖其他状态的派生属性,不建议直接存在state里,而是通过**Selector(选择器)**实时计算。这种方式既能避免props drilling,又能保证状态一致性。

具体实现步骤

1. 精简初始状态,只存原始数据

保持slice初始状态只包含核心原始数据,去掉需要计算的派生属性:

interface AppSliceState {
  words: string;
  cursor: number;
}

const initialState: AppSliceState = {
  words: "",
  cursor: 0,
};

2. 创建Selector计算派生状态

在slice文件中定义Selector,从原始state中计算出目标派生属性。推荐使用Redux Toolkit提供的createSelector,它会缓存计算结果,只有当依赖的原始状态变化时才重新计算,优化性能:

import { createSelector } from '@reduxjs/toolkit';
import { RootState } from '../store'; // 导入你的根状态类型

// 基础Selector:获取当前slice的状态
const selectAppSlice = (state: RootState) => state.app;

// 计算已输入字符
export const selectTypedChars = createSelector(
  [selectAppSlice],
  (appState) => appState.words.substring(0, appState.cursor)
);

// 计算剩余字符
export const selectRemainingChars = createSelector(
  [selectAppSlice],
  (appState) => appState.words.substring(appState.cursor)
);

如果是简单场景,也可以直接写普通函数式Selector(无缓存,适合计算成本低的场景):

export const selectTypedChars = (state: RootState) => state.app.words.substring(0, state.app.cursor);
export const selectRemainingChars = (state: RootState) => state.app.words.substring(state.app.cursor);

3. 在组件中使用Selector获取派生状态

任何需要这两个值的组件,直接通过useSelector钩子获取即可,完全不需要props传递:

import { useSelector } from 'react-redux';
import { selectTypedChars, selectRemainingChars } from './appSlice';

function TypingDisplay() {
  const typedChars = useSelector(selectTypedChars);
  const remainingChars = useSelector(selectRemainingChars);

  return (
    <div className="typing-area">
      <span className="typed">{typedChars}</span>
      <span className="remaining">{remainingChars}</span>
    </div>
  );
}

不推荐直接存储派生状态的原因

如果强行把typedChars和remainingChars存在state里,会带来以下问题:

  • 状态冗余:原始的words和cursor已经包含了所有计算所需信息,存储派生状态属于重复数据
  • 一致性风险:每次words或cursor更新时,都要手动同步更新这两个派生属性,一旦遗漏就会出现状态不一致
  • 维护成本高:额外增加了需要维护的状态属性,逻辑复杂度上升

特殊场景的妥协方案

如果你的派生状态计算逻辑极复杂、且多个组件频繁调用导致性能问题,可以考虑把派生状态存在state中,但必须确保每次依赖状态变化时同步更新:

import { createSlice, PayloadAction } from '@reduxjs/toolkit';

const appSlice = createSlice({
  name: 'app',
  initialState: {
    words: "",
    cursor: 0,
    typedChars: "",
    remainingChars: "",
  } as AppSliceState,
  reducers: {
    setCursor: (state, action: PayloadAction<number>) => {
      state.cursor = action.payload;
      // 同步更新派生状态
      state.typedChars = state.words.substring(0, state.cursor);
      state.remainingChars = state.words.substring(state.cursor);
    },
  },
  extraReducers: (builder) => {
    // 假设fetchWords是获取文本的异步action
    builder.addCase(fetchWords.fulfilled, (state, action) => {
      state.words = action.payload;
      // 同步更新派生状态
      state.typedChars = state.words.substring(0, state.cursor);
      state.remainingChars = state.words.substring(state.cursor);
    });
  },
});

但这种方式仅作为性能优化的最后手段,优先推荐使用Selector方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 15:27:42