前言
略掉了这些内容:
- Server action,因为新特性跟升级无关
- Form,因为社区表单方案足够优秀且成熟
- Class component 相关设计,比如 str ref 和老 ctx
- 老 react-dom 的设计,比如 render 和 findDomNode
只谈重点。
函数组件
[LOW] 新的 Ref 设计
暂时没有兼容性问题,可以缓步重构。
// before
const Comp: FC<any> = forwardRef((props, ref) => {
return ...
})
Comp.displayName = "Comp"
// after
const Comp: FC<any> = ({ref}) => {
return ...
}
// 两者在新版 React 中等价,ref 默认会在 props 里,且不再需要 forwardRef
<Comp ref={ref} />
老写法估计还会兼容好几个大版本。
[LOW] 新的 Context 设计
暂时没有兼容性问题,可以缓步重构。
// before
const PageContext = createContext<{}>()
<PageContext.Provider />
// after
const PageContext = createContext<{}>()
<PageContext />
- 提供了 codemod 工具快速迁移
- 几乎无害,只有好处
- 老写法用的太多,没有几个大版本也不可能删掉
[MEDIUM] Ref 的 cleanup
好实践,建议用起来
组件的 ref 很适合用来执行 side effect,但是此前一直没有一个用于清除此 side effect 的地方,所以 19 就加了一个。
<video
ref={(ref) => {
// some stuff
ref.current.play();
return () => {
// 像 useEffect 一样的 cleanup
ref.current.stop();
};
}}
/>
- 对现有代码几乎没有影响
- 是好实践,之后写 ref 的时候应该尽量去用
[LOW] 内置了 helmet
了解即可。
现在支持在 head 里插 title
、meta
、script
、link
了:
const Page = () => {
return <>
// 在任何地方都可以,会被自动插入到 head 里
<title>ok<title>
</>
}
但这只是给库作者的 building block,写业务的时候还是需要 react-helmet
,所以就当这个功能没有吧。
[MEDIUM] use
好实践,但是用起来可能有些理解成本,建议观望一阵。
use
是一个用于消费 (读取) 资源 (resource) 的函数。注意,它只是函数,因此不用遵守 rule of hooks。
React 社区中一直都有资源概念,但是讨论了很久都没得出答案,这里只谈 React 19 中所定义的 resource,即 Context
和 Promise
。
Context
的用例:
const SomeCtx = createContext<any>();
// 在任意 react 组件树内
const SomeComp = () => {
const a = use(SomeCtx);
for (const i = 0; i <= 10; i++) {
// 是的,它不用遵守 rule of hooks,所以只要在组件树里,什么地方都能用
const c = use(SomeCtx);
}
};
Promise
的用例:
const fetchData = () => fetch("some url");
// 注意不能放在 component 里创建 promise,会让 react 无法处理
const promise = fetchData();
const SomeComp = () => {
const data = use(promise);
data instanceof Promise; // false
// use 在面对 Promise 的时候可以当作 await 来用
// 当 use 里面的 Promise 没有 resolve 的时候,use 会直接 throw 这个 promise,
// 此 Promise 被上层的 ErrorBoundary 捕获(try catch)之后,会让 Suspense 暂时显示 fallback
// 之后当 Promise resolve 的时候,这个组件被重新渲染,就始终拿到了 data 而非 Promise of data。
};
- 这些东西在 18 的时候就慢慢在测了,基本上稳定了
useContext
短期内不会受影响- ⚠️ 注意:即使
use
可以在 server component 里用,也没法用来 consume context,因为 context 在 server 拿不到
新 Hooks
[LOW] useOptimistic
好实践,但是用起来可能有些理解成本,建议观望一阵。
字面意思,用于实现乐观更新。
function ChangeName({ currentName }) {
const [optimisticName, setOptimisticName] = useOptimistic(currentName);
const submitAction = async (formData) => {
const newName = formData.get("name");
setOptimisticName(newName);
const updatedName = await updateName(newName);
// 如果这里 throw 了,optimisticName 会变回原来的名字
};
return (
<form action={submitAction}>
<p>Your name is: {optimisticName}</p>
<p>
<label>Change Name:</label>
<input
type="text"
name="name"
disabled={currentName !== optimisticName}
/>
</p>
</form>
);
}
类型变更
[MEDIUM] useRef
必须传一个参数,否则类型不对
会影响项目中的类型检查,建议尽快迁移。
哪怕是 undefined
也要传一个。
规则大概可以用两条来概述:
- 如果传
null
则是不可变的 ref - 否则要么传入初始值,要么传入
undefined
,这样创建的是可变 ref
const Comp = () => {
// 不可变 ref 一般用于拿 ref
const immuRef = useRef<HTMLInputElement>(null)
// 可变 ref 之后必须传 undefined
const timerRef = useRef<number | undefined>(undefined)
useEffect(() => {
// ts 报错
immuRef.current = ...
// ts 不报错
timerRef.current = window.setTimeout(() => {})
})
}
[HIGH] ReactElement
类型
会影响项目中的类型检查,建议尽快迁移。
这个类型主要是用于遗留代码的,所以项目中所有用到的地方都是误用。
因此这个类型被限制的更严格了,我们的做法不是去适应它,而是赶紧弃用它。
正确的用法是:
type SomeCompProps = {
// 而非 ReactElement
children: ReactNode;
};
// 或者前者这个 children 可以直接从 React.PropsWithChildren 直接组合出来
type SomeCompProps = {
// ...other props
} & PropsWithChildren;
[HIGH] 全局的 JSX
namespace 被删除
会影响项目中的类型检查,建议尽快迁移。
之后必须手动引入了。
// 必须手动引入
import type { JSX } from "react";
所有的项目中的 JSX.Element
几乎都是误用,可以跟上面一样可以换成 ReactNode
,因此…