React 并发特性实战指南

更新日期: 2025-11-04 阅读: 24 标签: 并发

react 的并发特性让应用更加流畅和响应迅速。这些特性包括 useTransition、useDeferredValue、Suspense 和 useoptimistic。它们协同工作,能够显著提升用户体验。下面通过实际示例和最佳实践来了解这些功能。


理解并发渲染

并发渲染是 React 其他并发特性的基础。在并发渲染之前,React 采用同步更新方式。当触发更新时,React 会阻塞主线程,直到整个组件树重新渲染完成。这可能导致界面卡顿,特别是在处理复杂组件时,用户输入会变得迟钝。

并发渲染引入了可中断的渲染机制。React 可以开始渲染更新,然后暂停去处理更紧急的任务(比如用户输入),之后再从中断的地方继续渲染。这样就不会阻塞用户操作。


useTransition:管理任务优先级

useTransition 可以将状态更新标记为非紧急任务,让 React 能够中断这些更新去处理更重要的用户操作。

基本用法

const [isPending, startTransition] = useTransition();

useTransition 不接收参数,返回一个数组。数组包含 isPending(表示是否有过渡任务在进行)和 startTransition(用于标记非紧急更新的函数)。

处理复杂渲染

当有复杂计算可能阻塞用户交互时,可以使用 useTransition 来降低优先级。

标签切换组件示例:

function TabButton({ children, onTabChange }) {
  const [isPending, startTransition] = useTransition();

  const handleClick = () => {
    startTransition(() => {
      onTabChange();
    });
  };

  return (
    <button 
      onClick={handleClick}
      style={{ 
        opacity: isPending ? 0.6 : 1,
        transition: 'opacity 0.2s'
      }}
    >
      {children}
    </button>
  );
}

处理异步操作

对于 api 调用等异步操作,useTransition 可以协调状态更新,避免界面闪烁。

删除按钮示例:

function DeleteButton({ itemId, onDelete }) {
  const [isPending, startTransition] = useTransition();

  const handleDelete = async () => {
    startTransition(async () => {
      await onDelete(itemId);
    });
  };

  return (
    <button
      onClick={handleDelete}
      disabled={isPending}
      style={{ 
        opacity: isPending ? 0.6 : 1 
      }}
    >
      {isPending ? '删除中...' : '删除'}
    </button>
  );
}

使用异步函数时,isPending 状态会自动管理,不需要手动设置加载状态。


Suspense:声明式加载

Suspense 提供了声明式的加载边界,可以与 React.lazy 配合实现代码分割,也可以用于异步数据加载。

基本用法

<Suspense fallback={<LoadingComponent />}>
  <AsyncComponent />
</Suspense>

Suspense 接收 fallback 属性(加载期间显示的组件)和可能异步加载的子组件。

代码分割

配合 React.lazy 实现组件级代码分割:

const HeavyComponent = React.lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>加载中...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

异步数据加载

为不同数据源设置独立的 Suspense 边界:

function UserDashboard({ userId }) {
  return (
    <div>
      <h1>用户面板</h1>
      <Suspense fallback={<div>加载用户信息...</div>}>
        <UserProfile userId={userId} />
      </Suspense>
      <Suspense fallback={<div>加载文章列表...</div>}>
        <UserPosts userId={userId} />
      </Suspense>
    </div>
  );
}

每个组件管理自己的数据,父组件通过 Suspense 统一处理加载状态。

结合 useTransition

将 useTransition 与 Suspense 结合,可以在导航时避免突兀的加载状态。

路由组件示例:

function AppRouter() {
  const [currentPage, setCurrentPage] = useState('home');
  const [isPending, startTransition] = useTransition();

  function navigate(page) {
    startTransition(() => {
      setCurrentPage(page);
    });
  }

  return (
    <div>
      <nav>
        <button onClick={() => navigate('home')}>首页</button>
        <button onClick={() => navigate('profile')}>个人资料</button>
        <button onClick={() => navigate('settings')}>设置</button>
      </nav>
      <Suspense fallback={<div>页面加载中...</div>}>
        <div style={{ 
          opacity: isPending ? 0.7 : 1,
          transition: 'opacity 0.2s'
        }}>
          <PageContent page={currentPage} />
        </div>
      </Suspense>
    </div>
  );
}

在 Next.js App Router 中,导航会自动使用 useTransition。需要注意的是,useTransition 只会等待足够长的时间来避免隐藏已显示的内容,不会等待所有嵌套的 Suspense 边界。


use:读取 Promise 和 Context

use API 可以读取 Promise 和 Context 值,与 Suspense 配合良好。与 Hooks 不同,use 可以在条件语句中调用。

基本用法

const data = use(promise);
const contextValue = use(Context);

use 接收 Promise 或 Context,返回解析后的值。对于 Promise,会暂停组件直到解析完成。

完整示例

function App() {
  const userPromise = fetchUser('/api/user/123');

  return (
    <Suspense fallback={<div>加载用户信息...</div>}>
      <UserProfile userPromise={userPromise} />
    </Suspense>
  );
}

function UserProfile({ userPromise }) {
  const user = use(userPromise);
  
  return (
    <div>
      <img src={user.avatar} alt={user.name} />
      <h2>{user.name}</h2>
    </div>
  );
}

use 会暂停 UserProfile 组件直到 Promise 解析完成,Suspense 在加载期间显示回退内容。


useDeferredValue:延迟更新

useDeferredValue 可以延迟更新依赖于频繁变化值的 UI 部分,在当前内容可见的同时准备新内容。

基本用法

const deferredValue = useDeferredValue(value);

useDeferredValue 接收一个值,返回一个延迟版本,在快速更新期间会暂时保持旧值。

处理复杂渲染

当用户输入触发复杂计算时,使用 useDeferredValue 保持输入响应性:

function FilteredList({ items }) {
  const [filter, setFilter] = useState('');
  const deferredFilter = useDeferredValue(filter);

  return (
    <div>
      <input 
        value={filter} 
        onChange={(e) => setFilter(e.target.value)}
        placeholder="搜索..."
      />
      <ExpensiveFilteredItems 
        items={items} 
        filter={deferredFilter} 
      />
    </div>
  );
}

const ExpensiveFilteredItems = React.memo(function ({ items, filter }) {
  const filteredItems = items.filter(item =>
    item.name.toLowerCase().includes(filter.toLowerCase())
  );

  return (
    <div>
      {filteredItems.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
});

使用 React.memo 包装复杂组件,确保只在 props 实际变化时重新渲染。

异步搜索

结合 useDeferredValue 和 Suspense 实现搜索功能:

function SearchApp() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);
  const isStale = query !== deferredQuery;

  return (
    <div>
      <input 
        value={query} 
        onChange={(e) => setQuery(e.target.value)}
        placeholder="搜索用户..."
      />
      <div style={{ 
        opacity: isStale ? 0.6 : 1,
        transition: 'opacity 0.2s'
      }}>
        <Suspense fallback={<div>搜索中...</div>}>
          <SearchResults query={deferredQuery} />
        </Suspense>
      </div>
    </div>
  );
}

function SearchResults({ query }) {
  if (!query) {
    return <div>请输入搜索关键词</div>;
  }

  const results = use(searchUsers(query));
  
  return (
    <div>
      {results.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  );
}

通过比较当前查询词和延迟版本,使用透明度变化提示用户搜索结果正在更新。


useOptimistic:乐观更新

useOptimistic 可以立即显示预期更新,而实际更新在后台进行。必须在 useTransition 中使用。

基本用法

const [optimisticState, addOptimistic] = useOptimistic(
  currentState,
  updateFunction
);

接收当前状态和更新函数,返回乐观状态和触发乐观更新的函数。

实时交互

点赞按钮的乐观更新示例:

function LikeButton({ post, onToggleLike }) {
  const [, startTransition] = useTransition();
  const [optimisticPost, setOptimisticPost] = useOptimistic(
    post,
    (currentPost, shouldLike) => ({
      ...currentPost,
      liked: shouldLike,
      likes: currentPost.likes + (shouldLike ? 1 : -1)
    })
  );

  const handleClick = () => {
    startTransition(async () => {
      setOptimisticPost(!optimisticPost.liked);
      await onToggleLike(post.id, !optimisticPost.liked);
    });
  };

  return (
    <button onClick={handleClick}>
      {optimisticPost.liked ? '是' : '否'} {optimisticPost.likes}
    </button>
  );
}

点击时图标和计数立即变化,不需要额外的加载状态。

表单提交

评论表单的乐观更新:

function CommentForm({ comments, onSubmit }) {
  const [optimisticComments, addOptimisticComment] = useOptimistic(
    comments,
    (currentComments, newComment) => [
      ...currentComments,
      { ...newComment, isSubmitting: true }
    ]
  );
  
  const formRef = useRef();

  const handleSubmit = async (formData) => {
    const commentText = formData.get('comment');
    
    addOptimisticComment({
      id: Date.now(),
      text: commentText
    });

    await onSubmit(commentText);
    formRef.current?.reset();
  };

  return (
    <div>
      <form ref={formRef} action={handleSubmit}>
        <textarea name="comment" required />
        <button type="submit">发布评论</button>
      </form>
      
      <div>
        {optimisticComments.map(comment => (
          <div 
            key={comment.id}
            style={{ 
              opacity: comment.isSubmitting ? 0.7 : 1 
            }}
          >
            {comment.text}
            {comment.isSubmitting && ' (发布中...)'}
          </div>
        ))}
      </div>
    </div>
  );
}

提交表单时评论立即显示,带有发布中状态。请求完成后,乐观评论会被真实数据替换。如果请求失败,useOptimistic 会自动回滚。


总结

React 的并发特性共同构建了更流畅的用户体验:

  • useTransition:创建低优先级的状态更新,在处理复杂任务和异步操作时保持界面响应

  • useDeferredValue:延迟渲染依赖频繁变化值的 UI 部分,防止界面卡顿

  • Suspense:为代码分割和异步操作提供声明式加载边界

  • useOptimistic:通过乐观更新让用户交互感觉更加即时

合理运用这些特性,可以显著提升 React 应用的性能和用户体验。在实际开发中,建议根据具体场景选择合适的并发特性,避免过度使用。

本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!

链接: https://fly63.com/article/detial/13132

PHP和Redis实现在高并发下的抢购及秒杀功能示例详解

抢购、秒杀是平常很常见的场景,面试的时候面试官也经常会问到,比如问你淘宝中的抢购秒杀是怎么实现的等等。抢购、秒杀实现很简单,但是有些问题需要解决,主要针对两个问题:

说一说数据库的并发控制

最近在看Elasticsearch时看到了并发控制,由此看到了新的并发控制方式。不得不说Elasticsearch相较于关系型数据库就是两种理论建立的数据存储体系,当然它们在并发控制上也相差甚远,各有千秋。

PHP-高并发和大流量的解决方案

在互联网时代,并发,高并发通常是指并发访问。也就是在某个时间点,有多少个访问同时到来。 高并发架构相关概念QPS (每秒查询率) : 每秒钟请求或者查询的数量,在互联网领域,指每秒响应请求数

处理高并发的一般思路

今天看见有人聊目前系统有2亿的PV,该如何优化?当我看到这个话题的时候,突然在想自己工作中也遇到了不少高并发的场景了,所以即兴发挥,在这里简单总结和分享下,欢迎指正和补充。

nodejs使用 eventproxy 控制并发

很多网站有并发连接数的限制,所以当请求发送太快的时候会导致返回值为空或报错。 安装依赖 express superagent cheerio eventproxy。新建app.js 抓取所有的url

PHP 并发场景的几种解决方案

在秒杀,抢购等并发场景下,可能会出现超卖的现象,在PHP语言中并没有原生提供并发的解决方案,因此就需要借助其他方式来实现并发控制。列出常见的解决方案有:

并发编程三要素:原子性,有序性,可见性

并发编程三要素:原子性: 一个不可再被分割的颗粒。原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。有序性: 程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)

通过Iterator控制Promise.all的并发数

异步是 js 一个非常重要的特性,但很多时候,我们不仅仅想让一系列任务并行执行,还想要控制同时执行的并发数,尤其是在针对操作有限资源的异步任务,比如文件句柄,网络端口等等。

nodejs如何解决高并发?

Node可以在不新增额外线程的情况下,依然可以对任务进行并发处理 —— Node.js是单线程的。它通过事件循环(event loop)来实现并发操作,对此,我们应该要充分利用这一点 —— 尽可能的避免阻塞操作

如何利用 JavaScript 实现并发控制

在开发过程中,有时会遇到需要控制任务并发执行数量的需求。例如一个爬虫程序,可以通过限制其并发任务数量来降低请求频率,从而避免由于请求过于频繁被封禁问题的发生。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!