React 19 新特性详解与实战教程
React 19带来了许多令人兴奋的新功能和性能改进。本教程将详细介绍 React 19 的核心新特性,并通过实际代码案例帮助您快速上手。
1. React 19 概览
React 19 的重点在于提升开发体验(DX)、性能和对现代 Web 应用复杂性的支持。它引入了几个旨在简化常见模式(如表单处理、乐观更新)的 API。
2. Action & State (useActionState)
useActionState 是一个全新的 Hook,旨在将一个异步的 "action" 函数与一个状态变量绑定在一起。它特别适用于表单提交,可以让你轻松地获取 action 的返回值作为组件的状态,并且内置了对 pending 状态的支持。
旧方式:
import { useState } from 'react';
// 一个典型的 action 函数
async function addToCart(productId) {
// 模拟 API 调用
await new Promise(resolve => setTimeout(resolve, 1000));
return { success: true, message: `Added product ${productId} to cart!`, timestamp: Date.now() };
}
function AddToCartButton({ productId }) {
const [isPending, setIsPending] = useState(false);
const [lastResult, setLastResult] = useState(null);
const handleAddToCart = async () => {
setIsPending(true);
try {
const result = await addToCart(productId);
setLastResult(result);
} finally {
setIsPending(false);
}
};
return (
<>
<button onClick={handleAddToCart} disabled={isPending}>
{isPending ? 'Adding...' : 'Add to Cart'}
</button>
{lastResult && <p>Last result: {lastResult.message}</p>}
</>
);
}
React 19 新方式 (useActionState):
import { useActionState } from 'react';
import { addToCart } from './actions'; // 假设 action 函数在此处定义
function AddToCartButton({ productId }) {
const [state, formAction, isPending] = useActionState(addToCart, null);
return (
<form action={formAction}>
{/* 通过隐藏字段将 productId 传递给 action */}
<input type="hidden" name="productId" value={productId} />
<button type="submit" disabled={isPending}>
{isPending ? 'Adding...' : 'Add to Cart'}
</button>
{state && <p>Last result: {state.message}</p>}
</form>
);
}
useActionState 让表单逻辑更加简洁和声明式。
3. useOptimistic - 乐观 UI 更新
乐观 UI 是一种提升用户体验的技术,即在等待服务器确认之前,先假设操作会成功,并立即更新 UI。useOptimistic Hook 为此提供了一流的支持。
案例:一个简单的评论列表
import { useState, useOptimistic } from 'react';
function CommentSection() {
const [comments, setComments] = useState([
{ id: 1, text: 'Great post!' },
{ id: 2, text: 'Thanks for sharing.' }
]);
const [optimisticComments, addOptimisticComment] = useOptimistic(
comments,
(state, newComment) => [...state, {id: Date.now(), text: newComment, optimistic: true }]
);
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const newCommentText = formData.get('comment');
// 先更新乐观 UI
addOptimisticComment(newCommentText);
try {
// 模拟发送到服务器
await new Promise(resolve => setTimeout(resolve, 1000));
// 成功后,更新真实状态
setComments(c => [...c, { id: Date.now(), text: newCommentText }]);
} catch (e) {
// 如果失败,需要手动恢复 UI
console.error("Failed to submit comment", e);
// ... 恢复逻辑 ...
}
};
return (
<div>
<h3>Comments</h3>
<ul>
{optimisticComments.map(comment => (
<li key={comment.id} style={{ opacity: comment.optimistic ? 0.6 : 1 }}>
{comment.text} {comment.optimistic ? '(Sending...)' : ''}
</li>
))}
</ul>
<form onSubmit={handleSubmit}>
<input type="text" name="comment" placeholder="Write a comment..." required />
<button type="submit">Post</button>
</form>
</div>
);
}
在这个例子中,用户提交评论后,列表会立刻显示新评论(带有淡化的样式表示它是乐观的),而不会等待服务器响应,大大提升了响应感。
4. Ref 改进 (useRef)
React 19 中,useRef 的行为变得更加一致和强大。现在,你可以安全地将 ref.current 设置为 null 或 undefined,而不会触发错误。更重要的是,useRef 现在可以接受一个函数作为初始值,该函数仅在 ref 被创建时调用一次。
新语法示例:
import { useRef, useEffect } from 'react';
function MyComponent() {
// 1. 可以安全地设置为 null
const nullableRef = useRef();
useEffect(() => {
// 一些操作...
nullableRef.current = null; // 这在以前可能导致问题
}, []);
// 2. 函数式初始化 (惰性初始化)
const expensiveValueRef = useRef(() => {
// 这个函数只在组件首次挂载时执行一次
console.log("Expensive calculation running...");
return performExpensiveCalculation(); // 假设这是一个昂贵的操作
});
return <div ref={nullableRef}>My Component</div>;
}
5. Form Actions 的增强
React 19 极大地增强了对 HTML <form> 元素的支持,使其与新的 useActionState 和 useOptimistic 等 Hook 配合得更好。<form> 现在可以接受一个 JavaScript 函数作为 action prop,而不是仅仅是一个 URL。
示例:
import { useActionState, useOptimistic } from 'react';
// 一个更新用户信息的 action
async function updateProfile(prevState, formData) {
const name = formData.get('name');
const email = formData.get('email');
// ... 调用 API
await new Promise(resolve => setTimeout(resolve, 500)); // 模拟延迟
return { success: true, message: `Updated profile for ${name}` };
}
function ProfileForm() {
const [state, formAction] = useActionState(updateProfile, null);
return (
<form action={formAction}>
<label htmlFor="name">Name:</label>
<input id="name" name="name" type="text" required />
<label htmlFor="email">Email:</label>
<input id="email" name="email" type="email" required />
<button type="submit">Update Profile</button>
{state?.message && <p>{state.message}</p>}
</form>
);
}
6. 错误边界与 Suspense 的改进
React 19 修复了一些关于错误边界和 Suspense 的边缘案例,并使它们的行为更加可预测。特别是,错误边界现在可以捕获在同一个渲染通道中由 Suspense fallback 引发的错误。
7. 其他值得注意的变更
<script>和<link>组件: 在服务端渲染(SSR)时,React 现在会自动优化<script>和<link>标签的注入,减少了客户端 hydration 不匹配的风险。- 组件返回值: 组件不再能返回
undefined。如果一个组件需要有条件地不渲染任何内容,它必须显式返回null。 - 性能: 内部实现进行了优化,提高了整体性能。
8. 实战案例:构建一个待办事项列表
让我们将学到的知识结合起来,创建一个带有乐观 UI 和表单提交的待办事项应用。
import { useState, useOptimistic, useActionState } from 'react';
// 模拟 API 调用
async function addTodo(formData) {
const text = formData.get('todo');
await new Promise(resolve => setTimeout(resolve, 500));
return { id: Date.now(), text, completed: false }; // 模拟返回新创建的 todo
}
function TodoApp() {
const [todos, setTodos] = useState([]);
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo) => [...state, { id: Date.now(), text: newTodo, completed: false, optimistic: true }]
);
const [state, formAction] = useActionState(async (prevState, formData) => {
const newTodo = await addTodo(formData);
setTodos(t => [...t, newTodo]);
return null;
}, null);
const toggleTodo = (id) => {
setTodos(currentTodos =>
currentTodos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
return (
<div>
<h1>My Todo List</h1>
<form action={formAction}>
<input name="todo" placeholder="What needs to be done?" required />
<button type="submit">Add Todo</button>
</form>
<ul>
{optimisticTodos.map(todo => (
<li
key={todo.id}
style={{
textDecoration: todo.completed ? 'line-through' : 'none',
opacity: todo.optimistic ? 0.6 : 1
}}
>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
{todo.text} {todo.optimistic ? '(Adding...)' : ''}
</li>
))}
</ul>
</div>
);
}
export default TodoApp;
这个案例展示了 useOptimistic 如何让添加待办事项的体验变得流畅,以及 useActionState 如何简化表单提交的逻辑。
总结
React 19 通过引入 useActionState 和 useOptimistic 等新 Hook,显著简化了常见的 UI 模式,如表单处理和乐观更新。同时,对 Refs 和 Form Actions 的改进也提升了开发体验和应用的健壮性。拥抱这些新特性,可以让您的 React 应用更加高效和用户友好。
共同学习,写下你的评论
评论加载中...
作者其他优质文章