千呼万唤的React19来了. 虽然现在还是beta版本,但是不耽误大家提前准备学习下
今天medium的订阅推送了一个React19的新特性,觉得很不错,提炼点精华的说下
React19 是4月25号推出的beta版,从版本号大家已经可以了解到这是React的一大版本的升级.这也标志着18版本进入收尾阶段了.因为现在是beta版本,所以还有很多不确定性的功能和问题
优化表单提交和更新(Form submissions and optimistic updates)
大家肯定都遇到到过也解决过这种需求,提交一个表单,如果提交失败返回错误提示,提交成功存储db‘s server. 举个简单的例子:
import React, {useState, useCallback} from 'react'
const submitForm = async () => {/** form submit */}
export function Component () {
const [formState, setFormState] = useState(null);
const [isPending, setIsPending] = useState(false);
const formAction = useCallback( async (e) => {
e.prevenDefault();
setIsPending(true);
try {
const result = await submitForm()
setFormState(result)
} catch (err) {
setFormState({message:'Failed to complete action'})
}
setIsPending(false)
},[])
// 这里使用的useCallback防止deep render
return (
<form onSubmit={formAction}>
{/** Form Template */}
</form>
)
}
代码很简单.概述下在React18中以非紧急的方式从一个试图过渡到另一个试图的过程就是transitions(过渡) .在React19中支持转场(过渡)异步函数.而去可以使用useTransitions这个hook挂载并在异步数据获取过程中管理加载指示符或占位符的显示。
1.1 useActionState
新的hooks useActionState
支持3个三个参数
1. action:当form表单trigger的时候调用的函数
2. initial state:表单初始化的状态
3.一个绝对路径:指明表单要跳转的页面link ---> optional的选配
返回值也有三个
1.state: 当前返回的表单状态
2. dispatch:从aciton tirgger的函数 (redux 熟悉的很好理解)
3.isPending:一个boolean类型的状态isPending
引用一张图:画的很好
当表单提交的时候,作为useActionState的第一参数(action)传递下的函数将被触发.并返回我们期望提交是否成功的状态.Action 的函数接受2个参数,一个当前的from状态和触发表单时的数据.
1.2 使用实例
import {useActionState} from 'react'
const submitForm = async (formData) => {
/* from submit fn that call API */
}
const action = async (currentState, formData) => {
try {
const result = await submitForm(formData)
return {message: result}
} catch (error) {
return {message: error.message}
}
}
export function Component () {
const [state, dispatch, isPending] = useActionState(action, null)
//...
}
这就是一个基本的架子.接下来完善下我们的isPending, state. dispatch.
1.3 <form> 的Action
在React19中,from 元素扩展了属性action 它能获取到一个提交触发的action的函数.也就是我们传递过来的dispatch
return (
<form action={dispatch}>
{/** ...*/}
</form>
)
完整代码:
import {useActionState} from 'react'
const submitForm = async (formData) => {
/* from submit fn that call API */
}
const action = async (currentState, formData) => {
try {
const result = await submitForm(formData)
return {message: result}
} catch (error) {
return {message: error.message}
}
}
export function Component () {
const [state, dispatch, isPending] = useActionState(action, null)
return (
<form action={dispatch}>
<input type="text" name="text" disabled={isPending} />
<button type="submit" disabled={isPending}>
Add Todo
</button>
{state.message && <h4>{state.message}</h4>}
</form>
)
}
不错吧,对比下React18的时候,是不是简洁很多.不必担心再忘记使用useCallback勾子了.
1.2 useFormStatus
在React19中新的加的一个勾子
获取上一次提交的状态信息
import{ useFormStatus} from 'react'
export function NestedComponent() {
// 获取表单信息
const {pending, data, method, action} = useFormStatus();
// 这里写个blog没提到的官网写法:
// const status = useFormStatus();
return (
/* tempalte */
)
}
来个🌰
userNameForm.js
import {useState, useMemo, useRef} from 'react';
import {useFormStatus} from 'react-dom';
export default function UsernameForm() {
const {pending, data} = useFormStatus();
return (
<div>
<h3>Request a Username: </h3>
<input type="text" name="username" disabled={pending}/>
<button type="submit" disabled={pending}>
Submit
</button>
<br />
<p>{data ? `Requesting ${data?.get("username")}...`: ''}</p>
</div>
);
}
App.js
import UsernameForm from './UsernameForm';
import { submitForm } from "./actions.js";
import {useRef} from 'react';
export default function App() {
const ref = useRef(null);
return (
<form ref={ref} action={async (formData) => {
await submitForm(formData);
ref.current.reset();
}}>
<UsernameForm />
</form>
);
}
这个app.js就可以用我们提到useActionState进行改造.
1.3 useOptimistic
提供了一个当与后端交互时,更快的和响应用户交互,提升了用户体验
demo:
import{ useOptimistic} from 'react'
export function Component({message, updateMessage}) {
const [optimisticMessage, setOptimisticeMessage] = useOptimistic(message)
const submitForm = async (fromData) => {
const newMessage = fromData.get('text')
// 在提交表单前设置
setOptimisticeMessage(newMessage)
const updateName = await submitToApi(newMessage)
updateMessage(updateName)
}
return (
<form action={submitForm}>
<p>{optimisticMessage}</p>
<input type="text" name='text' />
<button type="submit">Add Message</button>
</form>
)
}
当用户点击 “添加消息 ”按钮提交表单时,会触发 submitForm()函数。在启动 API 请求更新消息之前,会调用 setOptimisticMessage() 函数,并调用从表单数据中获取的新消息值。这会立即更新用户界面以反映乐观变化,从而为用户提供即时反馈。更新完成或出错时,React 会自动切换回消息显示内容.
use API
在React19中提供一个新的APi: use.
这是一个可以从Promises 或者上下文(context)中读取值的通用方法
例如在createContext中
import {use, createContext} from 'react'
const Context = createContext({data: `Hi Evan. React 19 is coming`})
function NextedChildComponent () {
const context = use(Context)
}
是不是跟useConext很像,但是不同的是use方法可以使用条件语句和循环中
import {use, createContext} from 'react'
const Context = createContext({data: `Hi Evan. React 19 is coming`})
function NextedChildComponent ({value}) {
if (value ) {
const context = use(Context)
}
}
use还可以和Suspense & errorbound(错误边界)一起用
import { use, Suspense } from "react";
function Message({ messagePromise }) {
const messageContent = use(messagePromise);
return <p>Here is the message: {messageContent}</p>;
}
export function MessageContainer({ messagePromise }) {
return (
<Suspense fallback={<p>⌛Downloading message...</p>}>
<Message messagePromise={messagePromise} />
</Suspense>
);
}
React Server Components
React 服务器组件(React Server Components)是 React 19 中引入的一项新功能,它允许我们创建在服务器上运行的无状态 React 组件。这些组件会在捆绑之前提前运行在不同于客户端应用程序(或服务器端渲染的服务器)的环境中。
由于 React 服务器组件能够在网络服务器上运行,因此它们可用于访问数据层,而无需与 API 交互!
import db from './database'
//React Server Component
async function BlogPost ({postId}) {
// Load blog post data from database
const post = await db.posts.get(postId);
// Load comments for the post from database
const comments = await db.comments.getPostIds(postId)
return (
<div>
<div>{post.title}</div>
<p>{post.content}</p>
<ul>
{post.comments.map((comment) => {
<li key={comment.id}>
<Comment {...comment} />
</li>
})}
</ul>
</div>
)
}
有了它,我们就不必公开 API 端点,也不必使用额外的客户端获取逻辑来将数据直接加载到我们的组件中。所有的数据处理都在服务器上完成。
请记住,服务器组件是在服务器而非浏览器上运行的。因此,它们无法使用传统的 React 组件 API,如 useState。要在 React 服务器组件设置中引入交互性,我们需要利用客户端组件来补充服务器组件,以处理交互性
补充下代码
"use client"
export function Comment ({id, text}) {
const [likes, setLikes] = useState(0)
function handleLikes () {
setLikes(likes +1)
}
return (
<>
<div>{text}</div>
<buttton onClicl={handleLikes}>like:{likes}</buttton>
</>
)
}
请注意上例中组件文件顶部的 “use client ”声明。在使用 React 服务器组件时,“use client ”表示该组件是客户端组件,这意味着它可以管理状态、处理用户交互并使用特定于浏览器的 API。该指令明确告诉 React 框架和捆绑程序将该组件与服务器组件区别对待,后者是无状态的,并且在服务器上运行。
另一方面,React 服务器组件是默认的,因此我们不会在服务器组件文件的顶部说明 “使用服务器”。相反,“use server ”应该只用于标记可以从客户端组件调用的服务器端函数。这些功能被称为服务器操作(Server Actions)。
React 服务器组件通过分离客户端和服务器之间的关注点,改变了我们构建 React 应用程序的方式。React 团队相信它们最终会被广泛采用,并改变我们构建 React 应用程序的方式