第 33 章:破坏性变更与移除——19 删掉了什么,为什么
React 19 是大版本,大版本的意义不只是加新功能,更是清账:把历史包袱、有隐患的 API、被新写法取代的东西一次性删掉。本章看它删了什么、为什么删。
先记住这一句:19 的删除分三类:被新写法替代的(ReactDOM.render、forwardRef)、本身有隐患的(字符串 ref)、维护成本高没人用的(propTypes、函数组件 defaultProps)。删除的标准是"留下它比删掉它更贵"。
一图流:三类删除
第一类:被替代的写法
ReactDOM.render / ReactDOM.hydrate 删除。第 1 章讲过,19 唯一的入口是 createRoot / hydrateRoot。render 的"直接往容器里渲染"没有返回根对象的概念,天生和并发不兼容,所以删掉,逼你用能"update/unmount"的根对象。这是"并发需要根"的必然结果。
forwardRef 废弃。第 2 章讲过,19 里 ref 成了普通 prop,函数组件直接收 props.ref,forwardRef 这个包装函数就没了存在意义。它没被删(兼容存量),但新代码不该再用。
React.createFactory 删除。JSX 转换普及后没人手写工厂函数了。
第二类:有隐患的
字符串 ref 删除。老写法 ref="name" 通过字符串定位实例,容易在组件间串位、难以类型化,是长期的安全和正确性隐患。19 直接移除,只保留 ref={fn} 和 ref={object}。
element.ref 访问废弃。第 2 章说过,元素的 ref 字段在 19 里成了带废弃警告的兼容 getter(真值在 props.ref)。这是为未来彻底移除 element.ref 铺路。
第三类:维护成本高
propTypes 移出 React。运行时类型检查的价值被 TypeScript 取代,而 propTypes 给每个元素解析、验证,是纯运行时开销。19 把它从 react 包里移除,需要的人去用独立方案。
函数组件的 defaultProps 删除。Class 组件仍支持,但函数组件上的 defaultProps 在 19 被移除(用默认参数代替)。原因:函数组件的默认值用 function Foo({x = 1}) 更符合 JS 习惯,defaultProps 是历史包袱。
findDOMNode 删除。它靠"从组件找到真实 DOM"的魔法路径,破坏了封装,也阻碍并发。用 ref 替代。
行为收紧:不是删,是更严
useRef必须传参:useRef()缺参数直接报错,防止忘传导致 undefined。act必须 await:测试里的act现在是异步 API,逼你把副作用 flush 干净。- 错误处理 API 变化:
onUncaughtError/onCaughtError/onRecoverableError成了 createRoot 的正式选项(第 1 章见过),componentDidCatch的边界行为更明确。
为什么值得
删除的共性:留下它,React 就要一直为它付运行时成本、维护成本、兼容成本。19 借着大版本把这些债还了:
- 运行时更轻(没有 propTypes 验证、没有字符串 ref 查找)。
- 并发更干净(没有和并发冲突的 legacy 入口)。
- 心智更简单(一个入口、一个 ref 写法、一种类型方案)。
存量代码迁移的痛,换来的是新代码不再背着这些历史包袱。
动手实验
- 试被删的 API:在 React 19 项目里跑
ReactDOM.render、字符串 ref、函数组件defaultProps,看控制台的报错/警告,对照"为什么删"。 - 看 forwardRef 废弃:
forwardRef包一个组件,dev 下看废弃警告,再用"ref 进 props"的新写法对比。 - 试 useRef 缺参:
useRef()不传参,看报错信息,理解"行为收紧"。
小结
- 19 的删除三类:被替代、有隐患、没人用。
- 被替代:
ReactDOM.render→createRoot、forwardRef→ref 进 props。 - 有隐患:字符串 ref、
element.ref访问废弃。 - 没人用:
propTypes移出、函数组件defaultProps删除、findDOMNode。 - 行为收紧:
useRef必传参、act必 await。 - 删除的标准是"留下比删掉更贵",换来更轻的运行时和更干净的心智。
- 第七部分到此收尾。下一步第八部分工程与周边(第 34~36 章,选讲)。