新React v19特性
服务器组件(RSC
):经过多年的开发,React
引入了服务器组件
,而不是需要借助Next.js
动作(Action
):Action
也将彻底改变我们与 DOM 元素的交互方式。
增强的 hooks
:引入了很多新 hooks,将改变我们的编码体验。
总结一下 19.0.0-Beta 版本的发布的特性就是:
- 一个 Actions
- 三个新 hook
- 一个新 API
- ref 和 context 用法更方便
- 其他支撑类更新、服务端能力更新
1.如何使用服务器组件
默认情况下,React中的所有组件都是客户端组件。只有使用
'use server'` 时,组件才是服务器组件。
我们只需要将 'use server'
添加为组件的第一行即可。这将使组件成为服务器组件。它不会在客户端运行,只会在服务器端运行。
'use server';
export default async function requestUsername(formData) {
const username = formData.get('username');
if (canRequest(username)) {
// ...
return 'successful';
}
return 'failed';
}
2.动作(Action
)
什么是Action
使用异步转换的函数被称为Action
(动作)。Action
自动管理数据的提交:
- Pending状态:Action 提供了一个 state
-
- 请求开始时,代表对应的状态 -
pending状态
- 请求开始时,代表对应的状态 -
-
- 请求结束时,状态自动重置
Optimistic
更新:Action
支持新的useOptimistic
hook,因此我们可以在请求提交时向用户显示即时反馈。
- 错误处理:
Action
提供错误处理,因此我们可以在请求失败时显示Error Boundary
,并自动恢复Optimistic更新
为其原始值。
- 增强表单操作:
- 元素支持将函数传递给 action 和 formAction props。
-
- 传递给
action props
的函数默认使用Action
机制,并在提交后自动重置表单
- 传递给
Action
将允许我们将action
与<form/>标签
集成。简单来说,我们将能够用action
替换 onSubmit
事件。
在使用Action之前
在下面的代码片段中,利用 onSubmit
事件,在表单提交时触发搜索操作。
<form onSubmit={search}>
<input name="query" />
<button type="submit">查询</button>
</form>
使用Action之后
随着服务器组件
的引入, Action
可以在服务器端执行。在 JSX
中,我们可以删除 <form/>
的 onSubmit
事件,并使用 action
属性。action
属性的值将是一个提交数据的方法,可以在客户端
或服务器端
提交数据。
我们可以使用Action
执行同步
和异步
操作,简化数据提交管理和状态更新。为的是使处理表单和数据更加容易。
"use server"
const submitData = async (userData) => {
const newUser = {
username: userData.get('username'),
email: userData.get('email')
}
console.log(newUser)
}
const Form = () => {
return <form action={submitData}>
<div>
<label>用户名</label>
<input type="text" name='username'/>
</div>
<div>
<label>邮箱</label>
<input type="text" name="email" />
</div>
<button type='submit'>提交</button>
</form>
}
export default Form;
在上面的代码中,submitData
是服务器组件中的Action
。form
是一个客户端组件,它使用 submitData
作为Action
。submitData
将在服务器上执行。
3.Hooks
在 React 19
中,我们使用 useMemo
、forwardRef
、useEffect
和 useContext
的方式将会改变。这主要是因为将引入一个新的 hook
,即 use
。
useMemo()
之前的写法
import React, { useState, useMemo } from 'react';
function ExampleComponent() {
const [inputValue, setInputValue] = useState('');
// 记住输入框是否为空的检查结果
const isInputEmpty = useMemo(() => {
console.log('检测输入框是否为空');
return inputValue.trim() === '';
}, [inputValue]);
return (
<div>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<p>{isInputEmpty ? 'Input 为空' : 'Input有值'}</p>
</div>
);
}
export default ExampleComponent;
之后的写法
import React, { useState } from 'react';
function ExampleComponent() {
const [inputValue, setInputValue] = useState('');
const isInputEmpty = () => {
console.log('检测输入框是否为空');
return inputValue.trim() === '';
});
return (
<div>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<p>{isInputEmpty ? 'Input 为空' : 'Input有值'}</p>
</div>
);
}
export default ExampleComponent;
我们可以看到在 React19
之后,我们不再需要自己来做记忆化,React19
将会在后台自动完成。
forwardRef()
ref
现在将作为props
传递而不是使用 forwardRef()
hook。
这将简化代码。因此,在 React19
之后,我们不需要使用 forwardRef()
。
之前的写法
import React, { forwardRef } from 'react';
const ExampleButton = forwardRef((props, ref) => (
<button ref={ref}>
{props.children}
</button>
));
之后的写法
ref
可以作为属性传递。不再需要 forwardRef()
。
import React from 'react';
const ExampleButton = ({ ref, children }) => (
<button ref={ref}>
{children}
</button>
);
新的 use() hook
这个 hook
将简化我们如何使用 promises
、async
代码和 context
。
const value = use(resource);
示例1:接收async函数
下面的代码是使用 use
hook 进行 fetch
请求的示例:
import { use } from "react";
const fetchUsers = async () => {
const res = await fetch("远程地址");
return res.json();
};
const UsersItems = () => {
const users = use(fetchUsers());
return (
<ul>
{users.map((user) => (
<div key={user.id} >
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
))}
</ul>
);
};
export default UsersItems;
fetchUsers
进行远程数据请求
- 我们使用
use
hook 执行fetchUsers
,而不是使用useEffect
或useState
hooks。
use
hook 的返回值是users
,其中包含GET
请求的响应(users)。
- 在
return
中,我们使用users
进行对应信息的渲染处理。
示例2:接收context对象
我们以后可以直接将context
对象传人到use()
中,从而达到将context
引入组件的目的。而不需要useContext()
了。
使用createContext定义全局变量
这里我们定义
import { createContext, useState, use } from 'react';
const ThemeContext = createContext();
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
在组件中使用use()获取context信息
const Card = () => {
// use Hook()
const { theme, toggleTheme } = use(ThemeContext);
return (
// 基于theme/toggleTheme 渲染页面或者执行对应的操作
);
};
const Theme = () => {
return (
<ThemeProvider>
<Card />
</ThemeProvider>
);
};
export default Theme
上面代码中有几点需要简单解释一下:
ThemeProvider
负责提供context
。
Card
是我们将消费context
的组件。为此,我们将使用新的 hookuse
来消费context
。
useFormStatus() hook
在 React19
中,我们还有新的 hooks
来处理表单状态
和数据
。这将使处理表单更加流畅和简单。将这些 hooks
与 Action
结合使用将使处理表单和数据更加容易。
React19
中的这个新 hook
将帮助我们更好地控制你创建的表单。它将提供关于上次表单提交的状态信息。
- useFormStatus Hook 必须在渲染在 <form> 内部的组件中调用。
- useFormStatus 只会返回父级 <form> 的状态信息,而不会返回同一组件或其子组件中任何其他 <form> 的状态信息。
基础语法
这是它的语法:
const { pending, data, method, action } = useFormStatus();
或者简化的版本:
const { status } = useFormStatus()
pending
:如果表单处于待处理状态,则为true
,否则为false
。
data
:一个实现了FormData
接口的对象,其中包含父<form>
提交的数据。
- method:HTTP方法 – GET,或 POST。
-
- 默认情况下将是 GET。
action
:一个函数引用。
案例展示
useFormStatus
是从react-dom
库中导出的
import { useFormStatus } from "react-dom";
function Submit() {
const status = useFormStatus();
return <button disabled={status.pending}>
{status.pending ? '正在提交...' : '提交完成'}
</button>;
}
// ==== 父组件 ==引入Submit ====
const formAction = async () => {
// 模拟延迟 3 秒
await new Promise((resolve) => setTimeout(resolve, 3000));
}
const FormStatus = () => {
return (
<form action={formAction}>
<Submit />
</form>
);
};
export default FormStatus;
解释一下:
Submit
通过useFormStatus
可以获取此时from
表单的提交状态,并基于一些状态渲染一些辅助信息
formAction
是执行异步提交的处理
在上面的代码中,当表单提交时,从 useFormStatus
hook 我们将获得一个 pending
状态。
- 当
pending
为true
时,UI 上会显示 "正在提交..." 文本。
- 一旦
pending
为false
,"正在提交..." 文本将被更改为 "提交完成"。
当我们想要知道表单提交的状态并相应地显示数据时,它会很有用。
useFormState() hook
React19
中的另一个新 hook 是 useFormState
。它允许我们根据表单提交的结果来更新状态。
语法
这是它的语法:
const [state, formAction] =
useFormState(
fn,
initialState,
permalink?
);
fn
:表单提交或按钮按下时要调用的函数。
initialState
:我们希望状态初始值是什么。它可以是任何可序列化的值。在首次调用操作后,此参数将被忽略。
permalink
:这是可选的。一个URL
或页面链接,如果fn
将在服务器上运行,则页面将重定向到permalink
。
这个 hook
将返回:
state
:初始状态将是我们传递给initialState
的值。
formAction
:一个将传递给表单操作的操作。此操作的返回值将在状态中可用。
案例展示
import { useFormState} from 'react-dom';
const FormState = () => {
const submitForm = (prevState, queryData) => {
const name = queryData.get("username");
console.log(prevState); // 上一次的from 的state
if(name === '柒八九'){
return {
success: true,
text: "前端开发者"
}
}
else{
return {
success: false,
text: "Error"
}
}
}
const [ message, formAction ] = useFormState(submitForm, null)
return <form action={formAction}>
<label>用户名</label>
<input type="text" name="username" />
<button>提交</button>
{message && <h1>{message.text}</h1>}
</form>
}
export default FormState;
解释一下
submitForm
是负责表单提交的方法。这是一个Action
。
- 在 submitForm中,我们正在检查表单的值。
-
prevState
:初始状态将为null
,之后它将返回表单的prevState
。
-
queryData
:用于获取此次操作中from表单
中对应key
的值
useOptimistic() hook
useOptimistic – React 中文文档
useOptimistic
也新发布的Hook
,它允许我们在异步操作时显示不同的状态。主要目的是让我们可以在等待异步操作结果的时候,先假设操作成功并更新状态,然后再根据实际结果来确认状态。
这个 hook
将帮助增强用户体验,并应该导致更快的响应。这对于需要与服务器交互的应用程序非常有用。
语法
以下是 useOptimistic
hook 的语法:
const [ optimisticX, addOptimisticX] = useOptimistic(state, updatefn)
例如,当响应正在返回时,我们可以显示一个optimistic状态,以便让用户获得即时响应。一旦服务器返回实际响应,optimistic状态
将被替换。
import { useOptimistic } from 'react';
function AppContainer() {
const [optimisticState, addOptimistic] = useOptimistic(
state,
// 更新函数
(currentState, optimisticValue) => {
// 使用乐观值
// 合并并返回新 state
}
);
}
其中:
这个状态被称为“乐观”状态是因为通常用于立即向用户呈现执行操作的结果,即使实际上操作需要一些时间来完成
参数
- state:初始时和没有挂起操作时要返回的值。
- updateFn(currentState, optimisticValue):一个函数,接受当前 state 和传递给 addOptimistic 的乐观值,并返回结果乐观状态。它必须是一个纯函数。updateFn 接受两个参数:currentState 和 optimisticValue。返回值将是 currentState 和 optimisticValue 的合并值。
返回值
- optimisticState:结果乐观状态。除非有操作挂起,否则它等于 state,在这种情况下,它等于 updateFn 返回的值。
- addOptimistic:触发乐观更新时调用的 dispatch 函数。它接受一个可以是任何类型的参数 optimisticValue,并以 state 和 optimisticValue 作为参数来调用 updateFn。