React 中的 defaultProps 是普通可变对象,需在赋值后、export 前显式调用 Object.freeze 冻结;嵌套对象需 deepFreeze,更安全的替代方案是使用函数返回默认值或 as const。

React 中的 defaultProps 本质是普通对象,不是冻结对象
很多人误以为 defaultProps 是 React 自动冻结的,其实不然。它只是组件函数上的一个静态属性,类型为普通 JavaScript 对象,可以被任意代码修改(比如 DemoOne.defaultProps.x = 100)。一旦在运行时被改写,后续所有未传 x 的调用都会拿到错误默认值,且无任何提示。
手动冻结 defaultProps 需在定义后立即调用 Object.freeze
必须显式调用 Object.freeze,且时机很关键:只能在赋值完成后、组件首次渲染前执行。常见错误是把它写在组件内部或 hook 里,那已经太晚了。
- ✅ 正确位置:
defaultProps赋值语句之后,export之前 - ✅ 示例:
const DemoOne = (props) => { /* ... */ };<br>DemoOne.defaultProps = { x: 0 };<br>Object.freeze(DemoOne.defaultProps);<br>export default DemoOne; - ❌ 错误写法:
Object.freeze({ x: 0 })直接赋值——这会丢失引用,React 不识别 - ❌ 错误时机:在
useEffect或组件函数体内调用,此时 defaultProps 已被 React 读取并缓存
冻结后无法修改,但校验和运行时行为不受影响
Object.freeze 只阻止属性写入、删除和新增,不影响 React 内部对 defaultProps 的读取逻辑,也不干扰 PropTypes 校验流程。你可以继续用 PropTypes.string.isRequired 或 TypeScript interface 做类型约束,二者与冻结互不冲突。
使用 @ainative/react-sdk 为 React 应用添加 AI 聊天和积分。适用于 (1) 安装 @ainative/react-sdk,(2) 使用 useChat hook 实现聊天完成。
- 冻结后尝试修改会静默失败(非严格模式)或抛
TypeError(严格模式) - 仍可通过解构或展开运算符提取默认值:
{ x = props?.x ?? DemoOne.defaultProps.x } - 注意:如果 defaultProps 包含嵌套对象(如
{ config: { timeout: 5000 } }),Object.freeze不递归冻结,需手动处理或用deepFreeze
更安全的替代方案:用函数返回默认值
比起依赖可变对象,直接让默认值不可变更可靠。把 defaultProps 改成 getter 或工厂函数,从根本上杜绝修改可能。
- ✅ 推荐写法:
const getDefaultProps = () => ({ x: 0 });<br>const DemoOne = (props) => {<br> const { x = getDefaultProps().x } = props;<br> return <div>{x}</div>;<br>}; - ✅ TypeScript 用户可配合
as const:const defaultProps = { x: 0 } as const,获得编译期防护 - ⚠️ 注意:这种方式绕过了 React 对
defaultProps的自动合并逻辑,需自行处理 props 合并(尤其涉及...rest时)
真正容易被忽略的是嵌套结构的浅冻结问题——Object.freeze 不递归,而多数人写的默认配置恰恰是多层对象。不处理深层可变性,等于只锁了门没锁抽屉。

















