useTransition

useTransition 是一个 React Hook,可让你在后台渲染部分 UI。

¥useTransition is a React Hook that lets you render a part of the UI in the background.

const [isPending, startTransition] = useTransition()

参考

¥Reference

useTransition()

在组件的顶层调用 useTransition 将某些状态更新标记为转换。

¥Call useTransition at the top level of your component to mark some state updates as Transitions.

import { useTransition } from 'react';

function TabContainer() {
const [isPending, startTransition] = useTransition();
// ...
}

请参阅下面的更多示例。

¥See more examples below.

参数

¥Parameters

useTransition 没有参数。

¥useTransition does not take any parameters.

返回

¥Returns

useTransition 返回一个恰好包含两项的数组:

¥useTransition returns an array with exactly two items:

  1. isPending 标志告诉你是否有待处理的转换。

    ¥The isPending flag that tells you whether there is a pending Transition.

  2. 可让你将更新标记为 Transition 的 startTransition 函数

    ¥The startTransition function that lets you mark updates as a Transition.


startTransition(action)

useTransition 返回的 startTransition 函数可让你将更新标记为 Transition。

¥The startTransition function returned by useTransition lets you mark a updates as a Transition.

function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');

function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
// ...
}

注意

startTransition 中调用的函数称为 “操作”。

¥Functions called in startTransition are called “Actions”.

传递给 startTransition 的函数称为 “操作”。按照惯例,在 startTransition 内部调用的任何回调(例如回调 prop)都应命名为 action 或包含 “操作” 后缀:

¥The function passed to startTransition is called an “Action”. By convention, any callback called inside startTransition (such as a callback prop) should be named action or include the “Action” suffix:

function SubmitButton({ submitAction }) {
const [isPending, startTransition] = useTransition();

return (
<button
disabled={isPending}
onClick={() => {
startTransition(() => {
submitAction();
});
}}
>
Submit
</button>
);
}

参数

¥Parameters

  • action:通过调用一个或多个 set 函数 来更新某些状态的函数。React 立即调用 action,不带任何参数,并将 action 函数调用期间同步安排的所有状态更新标记为 Transitions。action 中等待的任何异步调用都将包含在转换中,但目前需要在额外的 startTransition 中封装 await 之后的任何 set 函数(参见 故障排除)。标记为 Transitions 的状态更新将是 non-blocking不会显示不必要的加载指示器

    ¥action: A function that updates some state by calling one or more set functions. React calls action immediately with no parameters and marks all state updates scheduled synchronously during the action function call as Transitions. Any async calls that are awaited in the action will be included in the Transition, but currently require wrapping any set functions after the await in an additional startTransition (see Troubleshooting). State updates marked as Transitions will be non-blocking and will not display unwanted loading indicators.

返回

¥Returns

startTransition 不返回任何东西。

¥startTransition does not return anything.

注意事项

¥Caveats

  • useTransition 是一个钩子,只能在组件内部或者自定义的钩子中调用。如果你需要在其他地方启动 Transition(例如,从数据库中启动),请改为调用独立的 startTransition

    ¥useTransition is a Hook, so it can only be called inside components or custom Hooks. If you need to start a Transition somewhere else (for example, from a data library), call the standalone startTransition instead.

  • 只有当你有权访问该状态的 set 函数时,你才能将更新封装到转换中。如果你想在响应某些属性或自定义钩子值时启动 Transition,请改为尝试 useDeferredValue

    ¥You can wrap an update into a Transition only if you have access to the set function of that state. If you want to start a Transition in response to some prop or a custom Hook value, try useDeferredValue instead.

  • 你传递给 startTransition 的函数会被立即调用,将其执行时发生的所有状态更新标记为 Transitions。例如,如果你尝试在 setTimeout 中执行状态更新,它们将不会被标记为 Transitions。

    ¥The function you pass to startTransition is called immediately, marking all state updates that happen while it executes as Transitions. If you try to perform state updates in a setTimeout, for example, they won’t be marked as Transitions.

  • 你必须在任何异步请求之后将任何状态更新封装在另一个 startTransition 中,以将它们标记为转换。这是一个已知的限制,我们将在未来修复(请参阅 故障排除)。

    ¥You must wrap any state updates after any async requests in another startTransition to mark them as Transitions. This is a known limitation that we will fix in the future (see Troubleshooting).

  • startTransition 函数具有稳定的标识,因此你经常会看到它从副作用依赖中省略,但包含它不会导致副作用触发。如果 linter 允许你在没有错误的情况下省略依赖,那么这样做是安全的。详细了解如何删除副作用依赖。

    ¥The startTransition function has a stable identity, so you will often see it omitted from Effect dependencies, but including it will not cause the Effect to fire. If the linter lets you omit a dependency without errors, it is safe to do. Learn more about removing Effect dependencies.

  • 标记为 Transition 的状态更新将被其他状态更新打断。例如,如果你在转换中更新图表组件,但在图表处于重新渲染过程中时开始输入输入,则 React 将在处理输入更新后重新启动图表组件上的渲染工作。

    ¥A state update marked as a Transition will be interrupted by other state updates. For example, if you update a chart component inside a Transition, but then start typing into an input while the chart is in the middle of a re-render, React will restart the rendering work on the chart component after handling the input update.

  • 转场更新不能用于控制文本输入。

    ¥Transition updates can’t be used to control text inputs.

  • 如果有多个正在进行的 Transition,React 当前会将它们批量处理在一起。这是一个可能会在未来版本中删除的限制。

    ¥If there are multiple ongoing Transitions, React currently batches them together. This is a limitation that may be removed in a future release.

用法

¥Usage

使用 Actions 执行非阻塞更新

¥Perform non-blocking updates with Actions

在组件顶部调用 useTransition 以创建操作并访问待处理状态:

¥Call useTransition at the top of your component to create Actions, and access the pending state:

import {useState, useTransition} from 'react';

function CheckoutForm() {
const [isPending, startTransition] = useTransition();
// ...
}

useTransition 返回一个恰好包含两项的数组:

¥useTransition returns an array with exactly two items:

  1. isPending 标志 告诉你是否有待处理的 Transition。

    ¥The isPending flag that tells you whether there is a pending Transition.

  2. startTransition 函数 可让你创建一个操作。

    ¥The startTransition function that lets you create an Action.

要启动转换,请像这样将函数传递给 startTransition

¥To start a Transition, pass a function to startTransition like this:

import {useState, useTransition} from 'react';
import {updateQuantity} from './api';

function CheckoutForm() {
const [isPending, startTransition] = useTransition();
const [quantity, setQuantity] = useState(1);

function onSubmit(newQuantity) {
startTransition(async function () {
const savedQuantity = await updateQuantity(newQuantity);
startTransition(() => {
setQuantity(savedQuantity);
});
});
}
// ...
}

传递给 startTransition 的函数称为 “操作”。你可以在操作中更新状态并(可选)执行副作用,并且工作将在后台完成,而不会阻止页面上的用户交互。Transition 可以包含多个 Action,当 Transition 正在进行时,你的 UI 保持响应。例如,如果用户单击一个选项卡,但随后改变主意并单击另一个选项卡,则第二次单击将立即处理,而无需等待第一次更新完成。

¥The function passed to startTransition is called the “Action”. You can update state and (optionally) perform side effects within an Action, and the work will be done in the background without blocking user interactions on the page. A Transition can include multiple Actions, and while a Transition is in progress, your UI stays responsive. For example, if the user clicks a tab but then changes their mind and clicks another tab, the second click will be immediately handled without waiting for the first update to finish.

为了向用户提供有关正在进行的转换的反馈,在第一次调用 startTransition 时,isPending 状态切换到 true,并保持 true,直到所有操作完成并向用户显示最终状态。转换确保操作中的副作用按 防止不必要的加载指示器 完成,并且你可以在转换正在进行时使用 useOptimistic 提供即时反馈。

¥To give the user feedback about in-progress Transitions, to isPending state switches to true at the first call to startTransition, and stays true until all Actions complete and the final state is shown to the user. Transitions ensure side effects in Actions to complete in order to prevent unwanted loading indicators, and you can provide immediate feedback while the Transition is in progress with useOptimistic.

The difference between Actions and regular event handling

例子 1 / 2:
在操作中更新数量

¥Updating the quantity in an Action

在此示例中,updateQuantity 函数模拟向服务器发出请求以更新购物车中的商品数量。此函数被人为地减慢速度,因此至少需要一秒钟才能完成请求。

¥In this example, the updateQuantity function simulates a request to the server to update the item’s quantity in the cart. This function is artificially slowed down so that it takes at least a second to complete the request.

快速多次更新数量。请注意,任何请求正在进行时都会显示待处理的 “总计” 状态,并且只有在最终请求完成后才会更新 “总计”。因为更新是在 Action 中,所以 “quantity” 可以在请求进行时继续更新。

¥Update the quantity multiple times quickly. Notice that the pending “Total” state is shown while any requests are in progress, and the “Total” updates only after the final request is complete. Because the update is in an Action, the “quantity” can continue to be updated while the request is in progress.

import { useState, useTransition } from "react";
import { updateQuantity } from "./api";
import Item from "./Item";
import Total from "./Total";

export default function App({}) {
  const [quantity, setQuantity] = useState(1);
  const [isPending, startTransition] = useTransition();

  const updateQuantityAction = async newQuantity => {
    // To access the pending state of a transition,
    // call startTransition again.
    startTransition(async () => {
      const savedQuantity = await updateQuantity(newQuantity);
      startTransition(() => {
        setQuantity(savedQuantity);
      });
    });
  };

  return (
    <div>
      <h1>Checkout</h1>
      <Item action={updateQuantityAction}/>
      <hr />
      <Total quantity={quantity} isPending={isPending} />
    </div>
  );
}

这是一个演示 Actions 如何工作的基本示例,但此示例不处理无序完成的请求。多次更新数量时,前面的请求可能会在后面的请求之后完成,从而导致数量更新无序。这是一个已知的限制,我们将在未来修复(请参阅下面的 故障排除)。

¥This is a basic example to demonstrate how Actions work, but this example does not handle requests completing out of order. When updating the quantity multiple times, it’s possible for the previous requests to finish after later requests causing the quantity to update out of order. This is a known limitation that we will fix in the future (see Troubleshooting below).

对于常见用例,React 提供内置抽象,例如:

¥For common use cases, React provides built-in abstractions such as:

这些解决方案为你处理请求排序。使用 Transitions 构建自己的自定义钩子或管理异步状态转换的库时,你可以更好地控制请求顺序,但你必须自己处理。

¥These solutions handle request ordering for you. When using Transitions to build your own custom hooks or libraries that manage async state transitions, you have greater control over the request ordering, but you must handle it yourself.


从组件公开 action prop

¥Exposing action prop from components

你可以从组件中公开 action prop,以允许父级调用操作。

¥You can expose an action prop from a component to allow a parent to call an Action.

例如,这个 TabButton 组件将其 onClick 逻辑封装在 action属性中:

¥For example, this TabButton component wraps its onClick logic in an action prop:

export default function TabButton({ action, children, isActive }) {
const [isPending, startTransition] = useTransition();
if (isActive) {
return <b>{children}</b>
}
return (
<button onClick={() => {
startTransition(() => {
action();
});
}}>
{children}
</button>
);
}

由于父组件在 action 内更新其状态,因此该状态更新被标记为转换。这意味着你可以单击 “帖子”,然后立即单击 “联系”,它不会阻止用户交互:

¥Because the parent component updates its state inside the action, that state update gets marked as a Transition. This means you can click on “Posts” and then immediately click “Contact” and it does not block user interactions:

import { useTransition } from 'react';

export default function TabButton({ action, children, isActive }) {
  const [isPending, startTransition] = useTransition();
  if (isActive) {
    return <b>{children}</b>
  }
  return (
    <button onClick={() => {
      startTransition(() => {
        action();
      });
    }}>
      {children}
    </button>
  );
}


显示待处理的视觉状态

¥Displaying a pending visual state

你可以使用 useTransition 返回的 isPending 布尔值向用户指示转换正在进行中。例如,选项卡按钮可以有一个特殊的 “挂起” 视觉状态:

¥You can use the isPending boolean value returned by useTransition to indicate to the user that a Transition is in progress. For example, the tab button can have a special “pending” visual state:

function TabButton({ action, children, isActive }) {
const [isPending, startTransition] = useTransition();
// ...
if (isPending) {
return <b className="pending">{children}</b>;
}
// ...

请注意,现在单击 “帖子” 感觉更加灵敏,因为选项卡按钮本身会立即更新:

¥Notice how clicking “Posts” now feels more responsive because the tab button itself updates right away:

import { useTransition } from 'react';

export default function TabButton({ action, children, isActive }) {
  const [isPending, startTransition] = useTransition();
  if (isActive) {
    return <b>{children}</b>
  }
  if (isPending) {
    return <b className="pending">{children}</b>;
  }
  return (
    <button onClick={() => {
      startTransition(() => {
        action();
      });
    }}>
      {children}
    </button>
  );
}


防止不需要的加载指示器

¥Preventing unwanted loading indicators

在此示例中,PostsTab 组件使用 use 获取一些数据。当你单击 “帖子” 选项卡时,PostsTab 组件会暂停,从而导致出现最接近的加载回退:

¥In this example, the PostsTab component fetches some data using use. When you click the “Posts” tab, the PostsTab component suspends, causing the closest loading fallback to appear:

import { Suspense, useState } from 'react';
import TabButton from './TabButton.js';
import AboutTab from './AboutTab.js';
import PostsTab from './PostsTab.js';
import ContactTab from './ContactTab.js';

export default function TabContainer() {
  const [tab, setTab] = useState('about');
  return (
    <Suspense fallback={<h1>🌀 Loading...</h1>}>
      <TabButton
        isActive={tab === 'about'}
        action={() => setTab('about')}
      >
        About
      </TabButton>
      <TabButton
        isActive={tab === 'posts'}
        action={() => setTab('posts')}
      >
        Posts
      </TabButton>
      <TabButton
        isActive={tab === 'contact'}
        action={() => setTab('contact')}
      >
        Contact
      </TabButton>
      <hr />
      {tab === 'about' && <AboutTab />}
      {tab === 'posts' && <PostsTab />}
      {tab === 'contact' && <ContactTab />}
    </Suspense>
  );
}

隐藏整个选项卡容器以显示加载指示器会导致不和谐的用户体验。如果你将 useTransition 添加到 TabButton,则可以改为在选项卡按钮中显示待处理状态。

¥Hiding the entire tab container to show a loading indicator leads to a jarring user experience. If you add useTransition to TabButton, you can instead display the pending state in the tab button instead.

请注意,单击 “帖子” 不再将整个选项卡容器替换为加载控件:

¥Notice that clicking “Posts” no longer replaces the entire tab container with a spinner:

import { useTransition } from 'react';

export default function TabButton({ action, children, isActive }) {
  const [isPending, startTransition] = useTransition();
  if (isActive) {
    return <b>{children}</b>
  }
  if (isPending) {
    return <b className="pending">{children}</b>;
  }
  return (
    <button onClick={() => {
      startTransition(() => {
        action();
      });
    }}>
      {children}
    </button>
  );
}

阅读有关将转换与 Suspense 结合使用的更多信息。

¥Read more about using Transitions with Suspense.

注意

转换仅 “等待” 足够长,以避免隐藏已经显示的内容(如选项卡容器)。如果 Posts 选项卡有 嵌套 <Suspense> 边界,则 Transition 不会为其 “等待”。

¥Transitions only “wait” long enough to avoid hiding already revealed content (like the tab container). If the Posts tab had a nested <Suspense> boundary, the Transition would not “wait” for it.


构建一个启用 Suspense 的路由

¥Building a Suspense-enabled router

如果你正在构建 React 框架或路由,我们建议将页面导航标记为 Transitions。

¥If you’re building a React framework or a router, we recommend marking page navigations as Transitions.

function Router() {
const [page, setPage] = useState('/');
const [isPending, startTransition] = useTransition();

function navigate(url) {
startTransition(() => {
setPage(url);
});
}
// ...

建议这样做有三个原因:

¥This is recommended for three reasons:

这是一个使用转换进行导航的简化路由示例。

¥Here is a simplified router example using Transitions for navigations.

import { Suspense, useState, useTransition } from 'react';
import IndexPage from './IndexPage.js';
import ArtistPage from './ArtistPage.js';
import Layout from './Layout.js';

export default function App() {
  return (
    <Suspense fallback={<BigSpinner />}>
      <Router />
    </Suspense>
  );
}

function Router() {
  const [page, setPage] = useState('/');
  const [isPending, startTransition] = useTransition();

  function navigate(url) {
    startTransition(() => {
      setPage(url);
    });
  }

  let content;
  if (page === '/') {
    content = (
      <IndexPage navigate={navigate} />
    );
  } else if (page === '/the-beatles') {
    content = (
      <ArtistPage
        artist={{
          id: 'the-beatles',
          name: 'The Beatles',
        }}
      />
    );
  }
  return (
    <Layout isPending={isPending}>
      {content}
    </Layout>
  );
}

function BigSpinner() {
  return <h2>🌀 Loading...</h2>;
}

注意

启用 Suspense 路由预计默认将导航更新封装到转换中。

¥Suspense-enabled routers are expected to wrap the navigation updates into Transitions by default.


向用户显示带有错误边界的错误

¥Displaying an error to users with an error boundary

如果传递给 startTransition 的函数抛出错误,你可以使用 误差边界 向用户显示错误。要使用错误边界,请将调用 useTransition 的组件封装在错误边界中。一旦函数传递到 startTransition 错误,就会显示错误边界的回退。

¥If a function passed to startTransition throws an error, you can display an error to your user with an error boundary. To use an error boundary, wrap the component where you are calling the useTransition in an error boundary. Once the function passed to startTransition errors, the fallback for the error boundary will be displayed.

import { useTransition } from "react";
import { ErrorBoundary } from "react-error-boundary";

export function AddCommentContainer() {
  return (
    <ErrorBoundary fallback={<p>⚠️Something went wrong</p>}>
      <AddCommentButton />
    </ErrorBoundary>
  );
}

function addComment(comment) {
  // For demonstration purposes to show Error Boundary
  if (comment == null) {
    throw new Error("Example Error: An error thrown to trigger error boundary");
  }
}

function AddCommentButton() {
  const [pending, startTransition] = useTransition();

  return (
    <button
      disabled={pending}
      onClick={() => {
        startTransition(() => {
          // Intentionally not passing a comment
          // so error gets thrown
          addComment();
        });
      }}
    >
      Add comment
    </button>
  );
}


故障排除

¥Troubleshooting

在 Transition 中更新输入不起作用

¥Updating an input in a Transition doesn’t work

你不能将转换用于控制输入的状态变量:

¥You can’t use a Transition for a state variable that controls an input:

const [text, setText] = useState('');
// ...
function handleChange(e) {
// ❌ Can't use Transitions for controlled input state
startTransition(() => {
setText(e.target.value);
});
}
// ...
return <input value={text} onChange={handleChange} />;

这是因为转换是非阻塞的,但响应更改事件更新输入应该同步发生。如果你想在输入时运行 Transition,你有两个选择:

¥This is because Transitions are non-blocking, but updating an input in response to the change event should happen synchronously. If you want to run a Transition in response to typing, you have two options:

  1. 你可以声明两个单独的状态变量:一个用于输入状态(始终同步更新),另一个用于在 Transition 中更新。这允许你使用同步状态控制输入,并将转换状态变量(将 “滞后” 输入)传递给其余渲染逻辑。

    ¥You can declare two separate state variables: one for the input state (which always updates synchronously), and one that you will update in a Transition. This lets you control the input using the synchronous state, and pass the Transition state variable (which will “lag behind” the input) to the rest of your rendering logic.

  2. 或者,你可以有一个状态变量,并添加 useDeferredValue,这将使 “滞后” 成为实际值。它将使用新值自动触发对 “赶上” 的非阻塞重新渲染。

    ¥Alternatively, you can have one state variable, and add useDeferredValue which will “lag behind” the real value. It will trigger non-blocking re-renders to “catch up” with the new value automatically.


React 不会将我的状态更新视为转换

¥React doesn’t treat my state update as a Transition

当你将状态更新封装在 Transition 中时,请确保它在 startTransition 调用期间发生:

¥When you wrap a state update in a Transition, make sure that it happens during the startTransition call:

startTransition(() => {
// ✅ Setting state *during* startTransition call
setPage('/about');
});

你传递给 startTransition 的函数必须是同步的。你不能像这样将更新标记为转换:

¥The function you pass to startTransition must be synchronous. You can’t mark an update as a Transition like this:

startTransition(() => {
// ❌ Setting state *after* startTransition call
setTimeout(() => {
setPage('/about');
}, 1000);
});

而是,你可以这样做:

¥Instead, you could do this:

setTimeout(() => {
startTransition(() => {
// ✅ Setting state *during* startTransition call
setPage('/about');
});
}, 1000);

React 不会将 await 之后的状态更新视为转换

¥React doesn’t treat my state update after await as a Transition

当你在 startTransition 函数中使用 await 时,await 之后发生的状态更新不会标记为转换。你必须在 startTransition 调用中的每个 await 之后封装状态更新:

¥When you use await inside a startTransition function, the state updates that happen after the await are not marked as Transitions. You must wrap state updates after each await in a startTransition call:

startTransition(async () => {
await someAsyncFunction();
// ❌ Not using startTransition after await
setPage('/about');
});

但是,改成这样是有效的:

¥However, this works instead:

startTransition(async () => {
await someAsyncFunction();
// ✅ Using startTransition *after* await
startTransition(() => {
setPage('/about');
});
});

这是由于 React 丢失了异步上下文的范围而导致的 JavaScript 限制。将来,当 AsyncContext 可用时,此限制将被删除。

¥This is a JavaScript limitation due to React losing the scope of the async context. In the future, when AsyncContext is available, this limitation will be removed.


我想从组件外部调用 useTransition

¥I want to call useTransition from outside a component

你不能在组件外部调用 useTransition,因为它是一个钩子。在这种情况下,请改用独立的 startTransition 方法。它的工作方式相同,但不提供 isPending 指标。

¥You can’t call useTransition outside a component because it’s a Hook. In this case, use the standalone startTransition method instead. It works the same way, but it doesn’t provide the isPending indicator.


我传递给 startTransition 的函数立即执行

¥The function I pass to startTransition executes immediately

如果运行此代码,它将打印 1、2、3:

¥If you run this code, it will print 1, 2, 3:

console.log(1);
startTransition(() => {
console.log(2);
setPage('/about');
});
console.log(3);

期望打印 1、2、3。你传递给 startTransition 的函数不会延迟。与浏览器 setTimeout 不同,它不会稍后运行回调。React 会立即执行你的函数,但在运行时安排的任何状态更新都标记为 Transitions。你可以想象它是这样工作的:

¥It is expected to print 1, 2, 3. The function you pass to startTransition does not get delayed. Unlike with the browser setTimeout, it does not run the callback later. React executes your function immediately, but any state updates scheduled while it is running are marked as Transitions. You can imagine that it works like this:

// A simplified version of how React works

let isInsideTransition = false;

function startTransition(scope) {
isInsideTransition = true;
scope();
isInsideTransition = false;
}

function setState() {
if (isInsideTransition) {
// ... schedule a Transition state update ...
} else {
// ... schedule an urgent state update ...
}
}

我的 Transitions 中的状态更新无序

¥My state updates in Transitions are out of order

如果你在 startTransition 内部使用 await,你可能会看到更新发生顺序错误。

¥If you await inside startTransition, you might see the updates happen out of order.

在此示例中,updateQuantity 函数模拟向服务器发出请求以更新购物车中的商品数量。此函数人为地返回前一个请求之后的每个其他请求,以模拟网络请求的竞争条件。

¥In this example, the updateQuantity function simulates a request to the server to update the item’s quantity in the cart. This function artificially returns the every other request after the previous to simulate race conditions for network requests.

尝试更新数量一次,然后快速更新多次。你可能会看到不正确的总数:

¥Try updating the quantity once, then update it quickly multiple times. You might see the incorrect total:

import { useState, useTransition } from "react";
import { updateQuantity } from "./api";
import Item from "./Item";
import Total from "./Total";

export default function App({}) {
  const [quantity, setQuantity] = useState(1);
  const [isPending, startTransition] = useTransition();
  // Store the actual quantity in separate state to show the mismatch.
  const [clientQuantity, setClientQuantity] = useState(1);
  
  const updateQuantityAction = newQuantity => {
    setClientQuantity(newQuantity);

    // Access the pending state of the transition,
    // by wrapping in startTransition again.
    startTransition(async () => {
      const savedQuantity = await updateQuantity(newQuantity);
      startTransition(() => {
        setQuantity(savedQuantity);
      });
    });
  };

  return (
    <div>
      <h1>Checkout</h1>
      <Item action={updateQuantityAction}/>
      <hr />
      <Total clientQuantity={clientQuantity} savedQuantity={quantity} isPending={isPending} />
    </div>
  );
}

多次单击时,先前的请求可能会在后续请求之后完成。发生这种情况时,React 目前无法知道预期的顺序。这是因为更新是异步安排的,而 React 会在异步边界上丢失顺序上下文。

¥When clicking multiple times, it’s possible for previous requests to finish after later requests. When this happens, React currently has no way to know the intended order. This is because the updates are scheduled asynchronously, and React loses context of the order across the async boundary.

这是预料之中的,因为转换内的操作不能保证执行顺序。对于常见用例,React 提供更高级的抽象,如 useActionState<form> 操作,为你处理排序。对于高级用例,你需要实现自己的排队和中止逻辑来处理这个问题。

¥This is expected, because Actions within a Transition do not guarantee execution order. For common use cases, React provides higher-level abstractions like useActionState and <form> actions that handle ordering for you. For advanced use cases, you’ll need to implement your own queuing and abort logic to handle this.


React 中文网 - 粤ICP备13048890号