译:使用模式匹配简化TanStack React Query状态处理

172 阅读2分钟

编者注:==本文介绍了一种使用模式匹配优雅处理TanStack React Query各种状态(加载中、错误、成功、空数据)的方法==。1) 通过matchQueryStatus工具函数将复杂的状态判断逻辑封装起来;2) 使组件代码更简洁、类型更安全;3) 遵循"沙漏模式"将复杂状态映射简化为两步处理。这种方法特别适合需要频繁处理异步状态的React应用。**

使用模式匹配简化TanStack React Query状态处理

==简单解决方案往往能产生重大影响==。在React巴黎大会上,我遇到一位React开发者说:"我记得你在LinkedIn上发过一篇关于React Query的帖子,现在我的同事都在用这个技巧。"我花了一些时间找到了这篇帖子,现在似乎是写博客详细说明的好时机。

回到正题:==异步数据获取是许多Web应用的核心部分==。TanStack React Query是管理服务器状态的优秀库。然而,根据查询状态(加载中、错误、成功、可能为空)渲染不同UI有时会导致组件内出现冗长繁琐的条件逻辑。

本文灵感来自Dominik Dorfmeister的文章《组合确实很棒》

挑战:处理多种查询状态

通常,使用useQuery时代码可能如下:

import { usePostsListQuery } from "./posts/queries";

export default function PostLists() {
  const postsQuery = usePostsListQuery();

  if (postsQuery.isLoading) {
    return <LoadingSpinner />;
  }

  if (postsQuery.isError) {
    return (
      <ErrorMessage error={postsQuery.error} refetch={postsQuery.refetch} />
    );
  }

  if (!postsQuery.data || postsQuery.data.length === 0) {
    return <EmptyMessage message="No posts found." />;
  }

  // 成功状态
  return (
    <ul>
      {postsQuery.data.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

虽然可行,但随着组件增长或需要在应用中重复处理这些状态时,嵌套的if语句或多个三元表达式会变得笨拙。

更简洁的方法:模式匹配工具

示例

我们的组件可以这样写:

import { usePostsListQuery } from "./posts/queries";
import { matchQueryStatus } from "./utils/matchQueryStatus";
import PostsListLayout from "./components/PostsListLayout";

export default function PostsList() {
  const postsQuery = usePostsListQuery();

  return (
    <PostsListLayout>
      {matchQueryStatus(postsQuery, {
        Loading: <LoadingSpinner />
        Errored: (error) => <ErrorMessage error={error.message} />,
        Empty: <EmptyMessage message="No posts found." />,
        Success: ({ data }) => (
          <ul>
            {data.map((post) => (
              <li key={post.id}>{post.title}</li>
            ))}
          </ul>
        )
      })}
    </PostsListLayout>
  );
}

很优雅,不是吗?所有复杂条件逻辑都消失了!

实现原理

这个工具函数将查询结果对象和状态映射选项对象作为参数:

import { type UseQueryResult } from "@tanstack/react-query";

export function matchQueryStatus<T>(
  query: UseQueryResult<T>,
  {
    Loading,
    Errored,
    Empty,
    Success,
  }: {
    Loading: JSX.Element;
    Errored: JSX.Element | ((error: unknown) => JSX.Element);
    Empty?: JSX.Element;
    Success: (data: UseQueryResult<T>) => JSX.Element;
  }
): JSX.Element {
  if (query.isLoading) {
    return Loading;
  }

  if (query.isError) {
    if (typeof Errored === "function") {
      return Errored(query.error);
    }
    return Errored;
  }

  const isEmpty =
    query.data === undefined ||
    query.data === null ||
    (Array.isArray(query.data) && query.data.length === 0);

  if (isEmpty && Empty) {
    return Empty;
  }

  return Success(query);
}

结论

使用matchQueryStatus模式匹配工具处理TanStack React Query结果有多个优点:

  1. ==可读性==:将状态处理逻辑整合为单一声明式结构
  2. ==可维护性==:更容易修改或添加特定状态处理
  3. ==可重用性==:可在不同组件中复用
  4. ==类型安全==:利用TypeScript提供更好的类型保证

代码可在Gist获取。

译者:Claude 3.5 Sonnet