Skip to content

React 19 新 Hooks

React 19 (2024.12) 引入的新 Hooks,2025 面试必考

1. use() Hook 🔥

1.1 核心概念

use() 是 React 19 最重要的新 Hook,统一了异步资源的读取方式

jsx
import { use } from 'react';

function Comments({ commentsPromise }) {
    // 直接读取 Promise!
    const comments = use(commentsPromise);
    
    return comments.map(c => <Comment key={c.id} {...c} />);
}

1.2 为什么需要 use()?

传统方式的问题

jsx
// ❌ 传统方式:useEffect + useState
function Comments({ id }) {
    const [comments, setComments] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);
    
    useEffect(() => {
        setLoading(true);
        fetchComments(id)
            .then(setComments)
            .catch(setError)
            .finally(() => setLoading(false));
    }, [id]);
    
    if (loading) return <Spinner />;
    if (error) return <Error />;
    return <CommentList comments={comments} />;
}

use() 的优势

jsx
// ✅ React 19:use() + Suspense
function Comments({ commentsPromise }) {
    const comments = use(commentsPromise);
    return <CommentList comments={comments} />;
}

// 父组件处理 loading 状态
<Suspense fallback={<Spinner />}>
    <Comments commentsPromise={fetchComments(id)} />
</Suspense>

1.3 use() 的特殊之处

可以在条件语句中使用(唯一可以的 Hook):

jsx
function User({ userPromise, shouldFetch }) {
    // ✅ use() 可以在条件中调用
    if (shouldFetch) {
        const user = use(userPromise);
        return <Profile user={user} />;
    }
    return <Guest />;
}

1.4 use() 读取 Context

jsx
// use() 也可以读取 Context
function ThemeButton() {
    // 等同于 useContext(ThemeContext)
    const theme = use(ThemeContext);
    
    // 但可以在条件中使用
    if (someCondition) {
        const theme = use(ThemeContext);
    }
    
    return <button className={theme}>Click</button>;
}

1.5 工作原理

┌─────────────────────────────────────────────────┐
│                use() 工作流程                    │
├─────────────────────────────────────────────────┤
│                                                 │
│  use(promise)                                   │
│      │                                          │
│      ├─ Promise pending ──→ 抛出 Promise        │
│      │                      (Suspense 捕获)     │
│      │                                          │
│      ├─ Promise fulfilled ─→ 返回结果           │
│      │                                          │
│      └─ Promise rejected ──→ 抛出错误           │
│                              (ErrorBoundary)    │
│                                                 │
└─────────────────────────────────────────────────┘

2. useActionState 🔥

2.1 核心概念

管理表单提交动作的状态,替代旧的 useFormState

jsx
import { useActionState } from 'react';

function LoginForm() {
    const [state, submitAction, isPending] = useActionState(
        async (previousState, formData) => {
            const email = formData.get('email');
            const password = formData.get('password');
            
            try {
                await login(email, password);
                return { success: true };
            } catch (error) {
                return { error: error.message };
            }
        },
        { error: null, success: false }  // 初始状态
    );
    
    return (
        <form action={submitAction}>
            <input name="email" type="email" />
            <input name="password" type="password" />
            
            {state.error && <p className="error">{state.error}</p>}
            
            <button disabled={isPending}>
                {isPending ? 'Logging in...' : 'Login'}
            </button>
        </form>
    );
}

2.2 API 说明

typescript
const [state, action, isPending] = useActionState(
    actionFn,      // (prevState, formData) => newState
    initialState,  // 初始状态
    permalink?     // 可选,用于 SSR
);

// 返回值:
// - state: 当前状态
// - action: 传给 form action 的函数
// - isPending: 是否正在执行

2.3 与 Server Actions 配合

jsx
// actions.js
'use server';

export async function createPost(prevState, formData) {
    const title = formData.get('title');
    
    if (!title) {
        return { error: 'Title is required' };
    }
    
    await db.posts.create({ title });
    revalidatePath('/posts');
    
    return { success: true };
}

// component.jsx
'use client';

import { createPost } from './actions';

function CreatePostForm() {
    const [state, action, isPending] = useActionState(createPost, {});
    
    return (
        <form action={action}>
            <input name="title" />
            {state.error && <span>{state.error}</span>}
            <button disabled={isPending}>Create</button>
        </form>
    );
}

3. useFormStatus 🔥

3.1 核心概念

获取父级 form 的提交状态,必须在 <form> 内部的组件中使用。

jsx
import { useFormStatus } from 'react-dom';

function SubmitButton() {
    const { pending, data, method, action } = useFormStatus();
    
    return (
        <button disabled={pending}>
            {pending ? 'Submitting...' : 'Submit'}
        </button>
    );
}

function MyForm() {
    return (
        <form action={submitForm}>
            <input name="name" />
            <SubmitButton />  {/* 必须是 form 的子组件 */}
        </form>
    );
}

3.2 API 说明

typescript
const status = useFormStatus();

// 返回对象:
{
    pending: boolean,  // 是否正在提交
    data: FormData,    // 提交的表单数据
    method: string,    // 提交方法 (GET/POST)
    action: Function   // 正在执行的 action
}

3.3 注意事项

jsx
// ❌ 错误:不能在 form 外部使用
function WrongUsage() {
    const status = useFormStatus();  // 报错!
    return (
        <form>
            <button disabled={status.pending}>Submit</button>
        </form>
    );
}

// ✅ 正确:必须在 form 的子组件中
function SubmitButton() {
    const status = useFormStatus();
    return <button disabled={status.pending}>Submit</button>;
}

function CorrectUsage() {
    return (
        <form action={...}>
            <SubmitButton />
        </form>
    );
}

4. useOptimistic 🔥

4.1 核心概念

实现乐观更新,在服务端响应前先更新 UI,失败时自动回滚。

jsx
import { useOptimistic } from 'react';

function Messages({ messages, sendMessage }) {
    const [optimisticMessages, addOptimisticMessage] = useOptimistic(
        messages,
        (state, newMessage) => [...state, { ...newMessage, sending: true }]
    );
    
    async function handleSubmit(formData) {
        const text = formData.get('text');
        
        // 立即显示(乐观更新)
        addOptimisticMessage({ text, sending: true });
        
        // 实际发送
        await sendMessage(text);
    }
    
    return (
        <>
            {optimisticMessages.map((msg, i) => (
                <div key={i} style={{ opacity: msg.sending ? 0.5 : 1 }}>
                    {msg.text}
                </div>
            ))}
            <form action={handleSubmit}>
                <input name="text" />
                <button>Send</button>
            </form>
        </>
    );
}

4.2 API 说明

typescript
const [optimisticState, addOptimistic] = useOptimistic(
    state,           // 真实状态
    updateFn         // (currentState, optimisticValue) => newState
);

// 工作流程:
// 1. 调用 addOptimistic(value) 立即更新 UI
// 2. 真实请求完成后,state 更新,optimisticState 自动同步
// 3. 如果请求失败,optimisticState 回滚到 state

4.3 典型场景:点赞功能

jsx
function LikeButton({ postId, likes, isLiked }) {
    const [optimisticLiked, setOptimisticLiked] = useOptimistic(isLiked);
    const [optimisticLikes, setOptimisticLikes] = useOptimistic(likes);
    
    async function handleLike() {
        // 乐观更新
        setOptimisticLiked(!optimisticLiked);
        setOptimisticLikes(optimisticLiked ? likes - 1 : likes + 1);
        
        // 实际请求
        await toggleLike(postId);
    }
    
    return (
        <button onClick={handleLike}>
            {optimisticLiked ? '❤️' : '🤍'} {optimisticLikes}
        </button>
    );
}

5. useFormState (已重命名)

⚠️ React 19 中 useFormState 已重命名为 useActionState

jsx
// ❌ 旧 API (React 18.3)
import { useFormState } from 'react-dom';

// ✅ 新 API (React 19)
import { useActionState } from 'react';

6. ref 作为 prop(不再需要 forwardRef)

6.1 React 19 的简化

jsx
// ❌ React 18:需要 forwardRef
const Input = forwardRef((props, ref) => {
    return <input ref={ref} {...props} />;
});

// ✅ React 19:ref 直接作为 prop
function Input({ ref, ...props }) {
    return <input ref={ref} {...props} />;
}

6.2 对比

jsx
// React 18
const Button = forwardRef(function Button({ children }, ref) {
    return <button ref={ref}>{children}</button>;
});

// React 19
function Button({ children, ref }) {
    return <button ref={ref}>{children}</button>;
}

// 使用方式相同
<Button ref={buttonRef}>Click</Button>

7. 其他 React 19 改进

7.1 Context 作为 Provider

jsx
// ❌ React 18
<ThemeContext.Provider value={theme}>
    {children}
</ThemeContext.Provider>

// ✅ React 19:Context 直接作为 Provider
<ThemeContext value={theme}>
    {children}
</ThemeContext>

7.2 文档元数据支持

jsx
// React 19:直接在组件中写 title、meta
function BlogPost({ post }) {
    return (
        <>
            <title>{post.title}</title>
            <meta name="description" content={post.summary} />
            <article>{post.content}</article>
        </>
    );
}

7.3 样式表支持

jsx
function Component() {
    return (
        <>
            <link rel="stylesheet" href="styles.css" precedence="default" />
            <div className="styled">Content</div>
        </>
    );
}

8. 面试高频问题

Q1: use() 和 useEffect + useState 的区别?

对比项useEffect + useStateuse()
代码量多(状态+加载+错误)
加载状态手动管理Suspense 处理
错误处理手动 catchErrorBoundary
数据获取时机组件内部可在组件外部
条件调用不支持支持

Q2: useActionState 和 useState + 手动处理的区别?

useActionState 优势

  1. 自动管理 pending 状态
  2. 与 form action 原生集成
  3. 支持 Server Actions
  4. 支持渐进增强(JS 禁用时仍可工作)

Q3: useOptimistic 什么时候用?

适用场景

  1. 点赞/收藏等高频交互
  2. 消息发送
  3. 评论提交
  4. 任何需要即时反馈但依赖服务端确认的操作

不适用场景

  1. 关键业务操作(支付、删除)
  2. 需要立即获取服务端响应的场景

Q4: 为什么 useFormStatus 必须在 form 子组件中使用?

因为 useFormStatus 通过 React 内部机制查找最近的父级 <form> 元素的提交状态。如果不在 form 内部,找不到关联的 form,无法获取状态。

Q5: React 19 移除 forwardRef 的原因?

简化 API

  1. forwardRef 增加了组件嵌套层级
  2. 学习成本高
  3. TypeScript 类型推断复杂
  4. ref 作为 prop 更直观、更统一

前端面试知识库