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

React useReducer中Symbol作为action type无法匹配switch/case的原因

React useReducer 使用Symbol作为action type报错原因解析

这个简单的计数器按钮可以正常工作,初始值设为0,每次点击都会递增按钮上的数值:

function Counter() {
    const INCREMENT = 'INC';
    const [state, dispatch] = React.useReducer(reducer, { counter: 0 });

    function reducer(state, action) {
        switch (action.type) {
            case INCREMENT:
                return { counter: state.counter + 1 };
            default:
                throw new Error("Unknown action!");
        }
    }

    return <button onClick={() => dispatch({ type: INCREMENT })}>{state.counter}</button>;
}

但如果将INCREMENT常量改为Symbol类型,每次点击按钮都会抛出“Unknown action!”错误:

function Counter() {
    const INCREMENT = Symbol();
    const [state, dispatch] = React.useReducer(reducer, { counter: 0 });

    function reducer(state, action) {
        switch (action.type) {
            case INCREMENT:
                return { counter: state.counter + 1 };
            default:
                throw new Error("Unknown action!");
        }
    }

    return <button onClick={() => dispatch({ type: INCREMENT })}>{state.counter}</button>;
}

原因解析

核心原因是React在处理useReducer的dispatch时,会对action对象进行序列化/克隆处理,结合Symbol的特性导致匹配失败:

  • Symbol的核心特性是唯一性:每次调用Symbol()都会生成一个全新的、独一无二的Symbol实例,哪怕是描述符相同的Symbol也不相等。
  • 当INCREMENT定义在组件内部时,组件每次重新渲染(比如点击按钮触发状态更新),都会重新创建这个Symbol实例。
  • React在dispatch action时,会对action对象进行内部处理(比如并发模式下的克隆、序列化),导致reducer接收到的action.type和当前组件内的INCREMENT已经不是同一个Symbol实例。
  • 由于Symbol的相等判断基于实例本身,switch语句中的case INCREMENT永远无法匹配,最终进入default分支抛出错误。

而使用字符串类型时,字符串是值类型,每次渲染时INCREMENT = 'INC'都是同一个值,dispatch的action.type和reducer中的判断值完全相等,所以匹配正常。

解决办法

将Symbol定义在组件外部,确保组件每次渲染时复用同一个Symbol实例:

const INCREMENT = Symbol(); // 定义在组件作用域外部
function Counter() {
    const [state, dispatch] = React.useReducer(reducer, { counter: 0 });

    function reducer(state, action) {
        switch (action.type) {
            case INCREMENT:
                return { counter: state.counter + 1 };
            default:
                throw new Error("Unknown action!");
        }
    }

    return <button onClick={() => dispatch({ type: INCREMENT })}>{state.counter}</button>;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 23:35:26