如何为Redux状态项生成新键?ID生成逻辑位置最佳实践咨询
React/Redux添加新状态项时的ID生成最佳实践
嘿,我来帮你理清楚这个问题——在给React/Redux状态添加带新键的新项时,ID生成的最佳实践其实要看场景,但大部分情况下有更稳妥的选择。先看你的问题:
给定初始状态:
const initialState = { "routines": { "1": { "id": 1, "name": "Routine 1", }, "2": { "id": 2, "name": "Routine 2", } } }
你纠结的是:生成新ID的逻辑该放在哪里?
- 是触发
ADD_ROUTINE动作的表单组件里? - 还是放在reducer中?
先看你当前的尝试方案:
import { ADD_ROUTINE } from '../actions/routineActions' function routineReducer(state={}, action) { switch (action.type) { case ADD_ROUTINE: newState = {...state} number_of_keys = Object.keys(newState).length new_id = number_of_keys + 1 return { ...state, [new_id]: { id: new_id, name: action.name, days: [] } } default: return state } } export default routineReducer
两种方案的分析与最优实践
先给你个明确结论:优先在触发动作的组件(或action creator)里生成ID,而不是放在reducer中,原因如下:
1. 为什么不推荐在reducer里生成ID?
你当前的写法有几个潜在的坑:
- ID重复风险:如果删除过某个routine,
Object.keys(newState).length就和实际最大ID不匹配了。比如删了id=2的项,此时键的数量是1,生成的新ID是2,但如果之后又添加,就会覆盖之前可能存在的项(或者逻辑出错)。 - 违背Reducer纯函数原则:Redux的reducer必须是纯函数——相同的输入必须返回相同的输出。如果在reducer里依赖当前状态生成ID,那相同的
ADD_ROUTINE动作,在不同的状态下会生成不同的ID,这会让调试(尤其是Redux DevTools的时间旅行功能)变得混乱,你没法复现相同的状态变化序列。
2. 在组件/Action Creator里生成ID的优势
把ID生成逻辑移到组件或者action creator里,好处多多:
- 避免状态依赖:可以生成完全独立于当前状态的唯一ID,比如用
uuid库生成标准的UUID,或者用Date.now()+随机数的方式,彻底杜绝ID重复的问题。 - 保持Reducer的纯粹性:Reducer只需要接收现成的ID,负责把新项合并到状态里即可,逻辑更单一,也符合Redux的设计理念。
优化后的示例代码
比如在action creator里生成ID(推荐):
// routineActions.js import { ADD_ROUTINE } from './actionTypes' import { v4 as uuidv4 } from 'uuid' // 先执行 npm install uuid export const addRoutine = (name) => ({ type: ADD_ROUTINE, payload: { id: uuidv4(), // 生成全局唯一ID name, days: [] } })
对应的reducer就可以简化成这样:
import { ADD_ROUTINE } from '../actions/routineActions' function routineReducer(state = {}, action) { switch (action.type) { case ADD_ROUTINE: const { id, name, days } = action.payload return { ...state, [id]: { id, name, days } } default: return state } } export default routineReducer
如果不想引入第三方库,也可以用这种轻量方式生成唯一ID:
const newId = `${Date.now()}-${Math.floor(Math.random() * 1000)}`
特殊场景:必须用连续数字ID怎么办?
如果你的业务要求必须用连续递增的数字ID,那也别用键的数量来计算,应该取当前状态里的最大ID加1:
case ADD_ROUTINE: // 找出当前所有项的最大ID const maxId = Object.values(state).length ? Math.max(...Object.values(state).map(item => item.id)) : 0 const newId = maxId + 1 return { ...state, [newId]: { id: newId, name: action.name, days: [] } }
这种方式比依赖键的数量可靠得多,但还是要注意:这种写法会让reducer依赖当前状态,牺牲了纯函数的部分特性,所以尽量还是优先在外部生成ID。
内容的提问来源于stack exchange,提问作者Jordan Coil
相关产品推荐
相关产品推荐

