作者:黄轩 openInula核心贡献者/架构SIG成员
新增
use
use
作为React19更新感知最明显的变化,其主要作用是获取异步数据,use
可以用来读取context,promise对象。且与Hooks不同,use
可以被有条件地调用 。React社区未来计划支持在渲染中使用use
的更多方式
tsx
import {use} from 'react';
function Comments({commentsPromise}) {
// `use` will suspend until the promise resolves.
const comments = use(commentsPromise);
return comments.map(comment => <p key={comment.id}>{comment}</p>);
}
function Page({commentsPromise}) {
// When `use` suspends in Comments,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
)
}
use不支持在渲染中创建的Promise
tsx
function Comments({commentsPromise}) {
const fetchData = new Promise<string>(resolve => setTimeout(() => resolve('Hello React19')));
const comments = use(commentsPromise);
return comments.map(comment => <p key={comment.id}>{comment}</p>);
}
useOptimistic
乐观更新
是一种在用户界面(UI)和数据交互场景中使用的策略,主要用于提升用户体验。在许多应用程序中,当用户执行一个操作(如提交表单、添加或删除数据项等)时,通常需要向服务器发送请求来更新数据。传统的方式是等待服务器响应后再更新 UI。而乐观更新则是在发送请求的同时,假设服务器会成功处理这个请求,立即更新 UI 来反映这个变化。
使用场景
- 购物车应用添加商品:用户将商品添加到购物车时,购物车中的商品数量和总价会立刻更新,好像商品已经成功添加到购物车一样。随后服务器会验证这个添加操作是否合法(例如商品是否有库存、用户是否登录等)。如果服务器验证成功,这个更新就保持;如果失败,购物车的数量和总价会回滚到之前的状态。
- 即使通讯软件中发送消息:当用户发送一条消息时,消息会立即出现在聊天窗口的发送框下方,并且显示为 "已发送" 状态。这是一种乐观更新,因为软件假设消息能够成功发送到服务器并被接收方获取。
示例
tsx
function Thread({ messages, sendMessage }) {
const formRef = useRef();
async function formAction(formData) {
addOptimisticMessage(formData.get("message"));
formRef.current.reset();
await sendMessage(formData);
}
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [
...state,
{
text: newMessage,
sending: true
}
]
);
return (
<>
{optimisticMessages.map((message, index) => (
<div key={index}>
{message.text}
{!!message.sending && <small> (Sending...)</small>}
</div>
))}
<form action={formAction} ref={formRef}>
<input type="text" name="message" placeholder="Hello!" />
<button type="submit">Send</button>
</form>
</>
);
}
export default function App() {
const [messages, setMessages] = useState([
{ text: "Hello there!", sending: false, key: 1 }
]);
async function sendMessage(formData) {
const sentMessage = await deliverMessage(formData.get("message"));
setMessages((messages) => [...messages, { text: sentMessage }]);
}
return <Thread messages={messages} sendMessage={sendMessage} />;
}
当请求完成后optimisticMessages的值会被更新为messages,请求失败时optimisticMessages会回退到未更新的messages
useActionState
useActionState
是一个可以根据某个表单动作的结果更新 state 的 Hook,返回的action
需要在<form>
标签中触发
tsx
async function add(payload: { value: number }) {
return new Promise<{ value: number }>(resolve => {
setTimeout(() => {
resolve({ value: payload.value + 1 });
}, 2000);
});
}
function useActionStateDemo() {
const [state, action, isPending] = useActionState<{ value: number }>(add, { value: 0 });
return (
<form>
{isPending ? 'working' : <div>{state.value}</div>}
<button formAction={action}>plus value</button>
</form>
);
}
注意 该Hook在React19 RC中的名称为useFormState
变更
ref
ref
prop支持传入清理函数,当组件卸载时,React 将调用从 ref
回调返回的清理函数。这适用于 DOM refs,类组件的 refs,以及 useImperativeHandle
。
tsx
<input ref={(ref)=>{
// xxx
return ()=>{
// ref clean
}
}}>
注意 在React19之前的版本,卸载组件时ref会被置为null,现在如果提供了清理函数,React将不再会将ref置为null
注意:Ref函数中的隐式返回可能导致问题
diff
- <div ref={current => (instance = current)} />
+ <div ref={current => {instance = current}} />
Context
<Context>
可以直接作为渲染提供者,无需使用<Context.Provider>
在未来的版本中会废弃<Context.Provider>
的写法
forwardRef
React19中函数式组件可以直接使用Ref,不再需要forwardRef
函数
- Befor
tsx
const MyInput = forwardRef(function ({ placeholder }, ref) {
return <input placeholder={placeholder} ref={ref} />;
});
- Now
tsx
const MyInput = ({placeholder, ref}) => {
return <input placeholder={placeholder} ref={ref}>
}
在未来的版本中forwardRef
函数会废弃
useDeferredValue
React19支持useDeferredValue
传入第二个参数initialValue
,函数签名如下
typescript
export function useDeferredValue<T>(value: T, initialValue?: T): T;
使用该参数useDeferredValue
在首次渲染就可以延迟
useTransition
useTransition支持异步函数
await
之后的状态不会视为Transition
tsx
startTransition(async () => {
await someAsyncFunction();
// ❌ 不要在 await 之后调用 startTransition
setPage('/about');
});
tsx
startTransition(async () => {
await someAsyncFunction();
// ✅ await之后再使用Transition
startTransition(() => {
setPage('/about');
});
});
移除
string Refs
getChildContext
和contextTypes
ReactDOM
新增
<form> Action
在 React 19 中,Actions 与 react-dom 的新 <form>
特性进行了整合。现在,可以将函数作为<form>
、<input>
和<button>
元素的action和formAction属性,以便自动使用 Actions 提交表单:
tsx
<input action={action}/>
当<form>
的action成功执行时,React会自动为不受控组件重置表单状态。如果需要手动重置<form>
,React DOM API 提供了全新的requestFormReset
方法。
useFormStatus
用于获取上次表单提交状态信息 const { pending, data, method, action } = useFormStatus();
-
pending
:布尔值。如果为true
,则表示父级<form>
正在等待提交;否则为false
。 -
data
:实现了FormData interface
的对象,包含父级<form>
正在提交的数据;如果没有进行提交或没有父级<form>
,它将为null
。 -
method
:字符串,可以是'get'
或'post'
。表示父级<form>
使用GET
或POST
HTTP 方法 进行提交。默认情况下,<form>
将使用GET
方法,并可以通过method
属性指定。 -
action
:一个传递给父级<form>
的action
属性的函数引用。如果没有父级<form>
,则该属性为null
。如果在action
属性上提供了 URI 值,或者未指定action
属性,status.action
将为null
。
注意 该Hook只能在位于<form>
标签之下的函数式组件中使用
移除
unmountComponentAtNode
ReactDOM.findDOMNode
ReactDOM.render
ReactDOM.hydrate