React 19 来了!让我们来学习一下吧!

写在前面

文末有我在前端面试多年的经验文章,分享给大家!!!

今年 2 月,React 宣布发布版本 19,终于更新了一个已经静止了两年的版本号(React 19 来了,有什么新功能?React 团队成员 Andrew Clark 证实,新版本将在 3 月或 4 月发布。

正如他们所说,“最后期限是第一个生产力驱动因素”。现在是四月底,新版本 19.0.0-Beta 刚刚如期发布。

虽然它只是一个测试版,但它已经在社区中引起了相当大的兴奋:

  • 丹评论说:“他们做了什么。
  • 安德鲁·克拉克(Andrew Clark)评论道:“反应19:永远不要再转发Ref了。
  • Josh W. Comeau 评论说:“这里有很多很好的生活质量改进!

唯一令人失望的是,正如 React 成员 Lauren 所证实的那样,React 编译器再次被延迟了。它最初被命名为“React Forget”,考虑到它的延迟,这是一个合适的标题。

19.0.0-Beta 版本的功能包括:

1. 动作功能
2.三个新钩子
3.新的 API
4.简化 ref 和 context
的使用 5.各种支持性更新和增强的服务器端功能

让我们深入研究其中的每一个。

行动

操作不是特定的 API,而是简化请求中数据处理的方法的通用术语。有效的 Actions 设置应简化异步操作,使开发人员能够更专注于业务逻辑而不是状态管理。

下面是一个示例来说明 Actions 的作用。假设我们有一个表单,用户可以在其中更新他们的姓名。以前,我们可能会使用 useState 来手动管理表单状态、错误状态和提交状态。代码可能如下所示:

function UpdateName() {  const [name, setName] = useState(“”);  const [error, setError] = useState(null);  const [isPending, setIsPending] = useState(false);  const handleSubmit = async () => {    setIsPending(true);    const error = await updateName(name);    setIsPending(false);    if (error) {      setError(error);      return;    }    redirect("/path");  };  return (    <div>      <input value={name} onChange={(event) => setName(event.target.value)} />      <button onClick={handleSubmit} disabled={isPending}>        Update      </button>      {error && <p>{error}</p>}    </div>  );}

使用 React 19 的 Actions,这种情况得到了优化。现在,我们可以使用useTransition钩子来处理表单提交,它会自动管理挂起状态,使我们的代码更加整洁:

function UpdateName() {  const [name, setName] = useState(“”);  const [error, setError] = useState(null);  const [isPending, startTransition] = useTransition();  const handleSubmit = async () => {    startTransition(async () => {      const error = await updateName(name);      if (error) {        setError(error);        return;      }      redirect("/path");    });  };  return (    <div>      <input value={name} onChange={(event) => setName(event.target.value)} />      <button onClick={handleSubmit} disabled={isPending}>        Update      </button>      {error && <p>{error}</p>}    </div>  );}

在 handleSubmit 函数中,异步请求的逻辑封装在传递给 startTransition 的回调中。当调用 startTransition 时,React 会立即将 isPending 设置为 true,表明转换(请求)正在进行中。然后,React 在后台异步执行 startTransition 回调。一旦请求完成,React 会自动将 isPending 切换回 false。

此功能将 isPending 绑定到提交按钮的 disabled 属性,因此该按钮在请求期间会自动变为禁用状态,从而防止双重提交。

总结 React 的 Action 约定:

  • 命名约定:使用异步转换的函数可以称为“操作”。
  • 挂起状态:操作会自动管理数据提交的挂起状态。当启动请求时,挂起状态将自动激活,并在最终状态更新后重置。这可确保用户在提交数据时收到反馈,并在请求完成后清除待处理状态。
  • 乐观更新:操作支持乐观更新,在请求挂起时向用户显示正确的提交结果。如果请求最终失败,乐观更新将自动恢复到其原始状态。
  • 错误处理:操作包括内置错误处理。当请求失败时,您可以使用错误边界来显示错误消息。
  • 表单支持:元素现在支持将函数传递给 action 和 formAction 属性。通过将函数传递给 action 属性,您可以使用 Actions 处理表单提交,并在提交后自动重置表单。这简化了处理表单的过程,使其更加直观和高效。

三个新钩子

为什么要先讨论行动?因为基于 Actions,React 19 引入了三个新的钩子,每个钩子都旨在简化开发人员对状态的复杂处理。

使用乐观

useOptimistic 的主要目的是允许我们假设异步操作成功,并在等待实际结果时相应地更新状态。

下面是一个基本的使用示例:

import { useOptimistic } from ‘react’;function AppContainer() { const [optimisticState, addOptimistic] = useOptimistic( state, // updateFn (currentState, optimisticValue) => { // merge and return new state // with optimistic value } );}

在此设置中:

  • state:初始状态和未进行任何操作时返回的状态。
  • updateFn(currentState, optimisticValue):一个纯函数,它获取当前状态和 addOptimistic 传递的乐观值,返回合并后的乐观状态。
  • optimisticState:乐观状态。如果没有正在进行的操作,则等于状态;否则,它等于 updateFn 的结果。
  • addOptimistic:用于触发乐观更新的函数,接受任何类型的 optimisticValue 并将其传递给 updateFn

useOptimistic 的应用范围很广,例如表单提交、点赞、书签、删除以及其他需要立即反馈的场景。

下面是数据删除的乐观更新示例:

import React, { useState } from 'react';import { useOptimistic } from 'react';function AppContainer() {    // Default data  const [state, setState] = useState([    { id: 1, name: 'Item 1' },    { id: 2, name: 'Item 2' },    { id: 3, name: 'Item 3' },  ]);    // Define the update function, which updates the state based on the current state and the optimistic value (the ID of the item to be deleted)  const updateFn = (currentState, optimisticId) => {    return currentState.filter(item => item.id !== optimisticId);  };  const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);    // Delete item  const deleteItem = async (itemId) => {    // Optimistically update the UI first    addOptimistic(itemId);    // Simulate API request delay    setTimeout(() => {      // Assume this is the API delete call, update the actual state after completion      setItems(currentItems => currentItems.filter(item => item.id !== itemId));    }, 2000);  };  return (    <div>      <h1>Optimistically Deleting Items</h1>      <ul>        {optimisticState.map(item => (          <li key={item.id}>            {item.name} <button onClick={() => deleteItem(item.id)}>Delete</button>          </li>        ))}      </ul>    </div>  );}export default AppContainer;

useActionState

useActionState,以前称为 useFormState,在版本 19 中有了新名称,其返回参数也发生了变化(奇怪的是,React 还没有更新 useActionState 文档,他们认为开发者不看文档吗?

以下是useActionState的最新基本用法:

const [state, action, pending] = useActionState(fn, initialState, permalink?);

返回参数包括:

  • state:表示当前状态。在第一次渲染时,它等于初始状态 initialState。手术后,这将是最新的结果。
  • action:这是用于执行操作的函数。当调用此函数时,它将触发 'fn' 的执行并更新状态。
  • pending:这是一个新参数,一个布尔值,指示当前是否正在执行操作。如果操作正在进行中,则为;否则,它是错误的

参数为:

  • fn:这是一个函数,在调用操作时触发,随后返回一个新值。
  • initialState:这是初始值,如果没有初始值,则设置为 null。
  • permalink:这是一个可选的字符串参数,通常与服务器操作结合使用。

下面是将 useActionState 与表单操作结合使用以实现名称更新功能的示例。如果更新失败,则会出错

显示在页面上;如果成功,页面将重定向到更新的页面:

import { useActionState } from 'react';function ChangeName({ name, setName }) {  // Using useActionState to create a state associated with form operations  const [error, submitAction, isPending] = useActionState(    // First parameter: Form operation function    async (previousState, formData) => {      // Define the logic for form operations here      // This function will be called upon form submission      // It receives two parameters:      // - previousState: The previous state, initially null, then the return value of the last operation      // - formData: A form data object, can retrieve form fields using formData.get("name")      const error = await updateName(formData.get("name"));      // If there is an error during the operation, return the error message      if (error) {        return error;      }      // If the operation is successful, perform a redirection      redirect("/path");    },     // Second parameter: Initial state, set here as null since the initial state is not crucial    null  }  // Return the form along with related state and actions  return (    <form action={submitAction}>      <input type="text" name="name" defaultValue={name} />      <button type="submit" disabled={isPending}>        Submit      </button>      {/* Display error messages if any */}      {error && <p>{error}</p>}    </form>  );}

useFormStatus

useFormStatus 用于检索有关表单提交的状态信息。其基本用法如下:

const { pending, data, method, action } = useFormStatus();

哪里:

  • pending:一个布尔值,指示父<表单>当前是否正在提交。如果为 true,则表单正在提交过程中;否则,它是错误的
  • data:实现 FormData 接口的对象,包含当前正在提交的父 中的数据。如果没有正在进行的提交或没有父<表单>,则为 null
  • method:一个字符串值,指示父 使用的 HTTP 方法,可以是 get 或 post。
  • action:对传递给父 的 action 属性的函数的引用。如果没有父级 ,则此属性为 null

例如,开发人员可以在表单操作中使用 useFormStatus 检索表单状态:

import { useFormStatus } from "react-dom";import action from './actions';function Submit() {  const status = useFormStatus();  return <button disabled={status.pending}>Submit</button>}export default function App() {  return (    <form action={action}>      <Submit />    </form>  );}

这种方法可能让人感到既熟悉又新颖。如果它让你想起上下文,那么你就走在正确的轨道上。可以考虑将 useFormStatus 作为上下文提供程序的某些功能的替代,但语法更加简化。

关于使用 useFormStatus,请务必注意两点:

1. useFormStatus Hook 必须在 内呈现的组件中调用。
2. useFormStatus 仅返回父组件的状态信息,而不返回同一组件或其子组件中的任何其他的状态信息。

A New API — 使用

以前被归类为钩子,现在已在 React 19 的 API 下记录其用法,从而成为新的 API。

use 用于从组件内的资源中读取值,其中资源可以是 Promise 或上下文。

以下是其通常的工作原理:

import { use } from ‘react’;function MessageComponent({ messagePromise }) {  const message = use(messagePromise);  const theme = use(ThemeContext);  // …}

主要用于更高级别的框架,如 Next.js。例如,建议使用 async...如果在 Next.js 中的服务器组件中发生数据提取,则为 await 而不是 use。如果在客户端组件中进行获取,建议在服务器组件中创建一个 Promise,并将其作为 props 传递给客户端组件。

use 也可以与 Suspense boundaries 一起使用。如果调用 use 的组件包装在 Suspense 边界中,则将显示指定的回退。一旦 Promise 解决,回退将被返回的数据替换。如果 Promise 被拒绝,将显示最近的错误边界的回退。

简化了 ref 和上下文的使用

如果你主要使用 React 的客户端功能,那么本节中讨论的更改可能会让你最感兴趣。

Ref 丢弃 forwardRef

还记得被 forwardRef 支配的恐惧吗?在 React 19 中,我们终于可以丢弃 forwardRef。从现在开始,refs 可以作为 props 传递。

例如,假设我们有一个功能组件 TextInput,这是一个接受占位符文本的占位符属性的简单输入框。现在,我们希望获取对父组件中输入框的引用,以便在必要时专注于它。以下是您如何编写它:

import React, { useRef } from 'react';// Define a function component TextInputfunction TextInput({ placeholder, ref }) {  return <input placeholder={placeholder} ref={ref} />;}// Parent componentfunction ParentComponent() {  // Create a ref to store the input box's reference  const inputRef = useRef(null);  // In some event handler, obtain the input box's reference and focus on it  const focusInput = () => {    inputRef.current.focus();  };  return (    <div>      {/*         Pass the inputRef to the TextInput component,        allowing the ref to be used internally within the TextInput      */}      <TextInput placeholder="Enter your name" ref={inputRef} />      <button onClick={focusInput}>Focus Input</button>    </div>  );}export default ParentComponent;

这在心理上不是比使用 forwardRef 的负担要轻吗?

作为提供程序的上下文

从 React 19 开始,开发者可以直接使用 作为提供者,而不是使用 <Context.Provider>。

假设我们有一个用于管理主题信息的 ThemeContext。在 React 19 中,我们可以使用 作为提供者,如下所示:

import React, { createContext } from 'react';// Create a theme contextconst ThemeContext = createContext('');// App component as the theme providerfunction App({ children }) {  return (    <ThemeContext value="dark">      {children}    </ThemeContext>  );}

将来,Context.Provider 将被弃用和删除。

其他更新

此版本还包括对服务器端功能的各种支持特性和增强功能,这些功能在纯客户端 React 开发中不太常用,因此简要总结了一下:

  • 服务器组件和服务器操作正在成为稳定的功能。这些概念对于熟悉 Next.js/Remix 的人来说是众所周知的,但那些不使用这些框架的人通常不会使用这些概念。
  • useDeferredValue 已更新为包含第二个参数,可以选择用于指定初始值。useDeferredValue 的新用法现在是: const value = useDeferredValue(deferredValue, initialValue?);
  • 支持直接在 React 代码中编写文档元数据,即在页面组件中编写 、 和 标签会自动将它们添加到应用程序的 :
function BlogPost({post}) {  return (    <article>      <h1>{post.title}</h1>      <title>{post.title}</title>      <meta name="author" content="Josh" />      <link rel="author" href="<https://twitter.com/joshcstory/>" />      <meta name="keywords" content={post.keywords} />      <p>        Eee equals em-see-squared...      </p>    </article>  );}
  • 支持直接在 React 代码中编写样式表,即在页面组件中编写的标签会自动添加到中。
  • 支持编写标签,这些标签也会自动添加到中。
  • 支持预加载资源,也会自动添加到中:
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'function MyComponent() {  preinit('https://.../path/to/some/script.js', {as: 'script' }) // loads and executes this script eagerly  preload('https://.../path/to/font.woff', { as: 'font' }) // preloads this font  preload('https://.../path/to/stylesheet.css', { as: 'style' }) // preloads this stylesheet  prefetchDNS('https://...') // when you may not actually request anything from this host  preconnect('https://...') // when you will request something but aren't sure what}

结论

最后,让我引用 Huang Xuan 的话:“我从 React 中学到的最关键原则可能是在定义新的概念抽象时要无所畏惧,并且从不妥协这些定义的准确性和可组合性。

接下来给大家推荐一篇我在前端面试多年的经验文章,希望大家看完以后都可以领取到心仪的offer哦!

文章:《聊聊前端面试那些事儿》

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值