Redux是否允许dispatch时新增state字段?附reducer代码校验
Redux 相关问题解答
一、是否允许在reducer中新增初始状态未定义的字段
技术层面Redux没有做强制限制:Redux存储的状态本质是普通JavaScript对象,reducer返回什么结构的对象,store就会保存什么结构,不会自动做字段校验拦截,所以你确实可以写出动态新增字段的代码且代码能运行。
但这是Redux官方明确不推荐的反模式,会带来很多可预见的问题:
- 组件首次挂载读取状态时,未在初始state定义的字段会返回
undefined,极易引发渲染逻辑判断错误、甚至读取属性抛异常的问题 - 状态结构不稳定,Redux DevTools追踪状态变更历史时会出现结构跳变,排查问题难度大幅提升
- 做状态持久化、状态重置、TS类型校验、组件PropTypes校验时,会出现大量不符合预期的边界case
Redux最佳实践要求:初始state必须明确定义所有后续业务会用到的字段,给每个字段设置符合业务预期类型的初始值,禁止动态新增根级/嵌套state字段。
二、给出的userReducer代码是否符合规范、是否正确
这段代码不符合Redux规范,且存在多处明确的逻辑bug,不能直接在生产环境使用,核心问题如下:
- 初始状态定义缺失:初始值仅设置了
user: {},后续用到的loading、isAuthenticated、error字段都未在初始state中声明,组件首次渲染读取这几个字段时会拿到undefined,直接影响加载态、鉴权态的判断逻辑。 - 多个action分支直接覆盖全量state,丢失已有状态:
LOGIN_REQUEST/REGISTER_USER_REQUEST/LOAD_USER_REQUEST、LOGOUT_SUCCESS、LOAD_USER_FAIL这几个分支返回新对象时,没有用...state展开已有状态,直接把返回的新对象作为全量state,会把之前存在的user、error等字段直接抹除。比如用户登录成功后state中已经存储了用户信息,此时触发刷新用户信息的LOAD_USER_REQUEST动作,state会被直接覆盖为只有loading: true、isAuthenticated: false的对象,已存在的用户信息会直接丢失,页面读取用户属性时会直接抛错。 - 分支逻辑不一致:同类型的action处理逻辑不统一,比如所有失败分支中只有
LOAD_USER_FAIL没有展开旧state,后续维护时非常容易引入新bug。 - 字段类型不稳定:
user字段初始为空对象{},鉴权成功时存用户信息对象,失败/登出时又设为null;error字段初始为undefined,请求失败时存错误信息,清错误时设为null,字段类型频繁跳变,组件侧如果没有做冗余的空值判断很容易出问题。
修正后的参考写法
// 定义完整的初始状态,所有字段提前声明并设置符合类型的初始值 const initialState = { user: null, loading: false, isAuthenticated: false, error: null }; export const userReducer = (state = initialState, action) => { switch (action.type) { case LOGIN_REQUEST: case REGISTER_USER_REQUEST: case LOAD_USER_REQUEST: return { ...state, loading: true, isAuthenticated: false, }; case LOGIN_SUCCESS: case REGISTER_USER_SUCCESS: case LOAD_USER_SUCCESS: return { ...state, loading: false, isAuthenticated: true, user: action.payload, error: null }; case LOGOUT_SUCCESS: return { ...state, loading: false, user: null, isAuthenticated: false, error: null }; case LOGIN_FAIL: case REGISTER_USER_FAIL: case LOAD_USER_FAIL: return { ...state, loading: false, isAuthenticated: false, user: null, error: action.payload, }; case LOGOUT_FAIL: return { ...state, loading: false, error: action.payload, }; case CLEAR_ERRORS: return { ...state, error: null, }; default: return state; } };
内容的提问来源于stack exchange,提问作者Asit Purohit
相关产品推荐
相关产品推荐

