如何使用 React-Query 处理多个查询

问题描述 投票:0回答:5

我已经开始使用 React-Query,如果我只需要从数据库中的单个集合中获取数据,它会非常有用。但是,我正在努力寻找一种查询多个集合以在单个组件中使用的好方法。

一次查询(没问题):

const { isLoading, isError, data, error } = useQuery('stuff', fetchStuff)

if (isLoading) {
     return <span>Loading...</span>
   }
 
   if (isError) {
     return <span>Error: {error.message}</span>
   }
 
   return (
     <ul>
       {data.map(stuff => (
         <li key={stuff.id}>{stuff.title}</li>
       ))}
     </ul>
   )
 }

对不同集合的多个查询(😬):

const { isLoading: isLoadingStuff, isError: isErrorStuff, data: stuff, error: errorStuff } = useQuery('stuff', fetchStuff);
const { isLoading: isLoadingThings, isError: isErrorThings, data: Things, error: errorThings } = useQuery('things', fetchThings);
const { isLoading: isLoadingDifferentStuff, isError: isErrorDifferentStuff, data: DifferentStuff, error: errorDifferentStuff } = useQuery('DifferentStuff', fetchDifferentStuff);

const isLoading = isLoadingStuff || isLoadingThings || isLoadingDifferentStuff
const isError = isErrorStuff || isErrorThings || isErrorDifferentStuff
const error = [errorStuff, errorThings, errorDifferentStuff]

if (isLoading) {
     return <span>Loading...</span>
   }
 
if (isError) {
    return (
      <span>
        {error.forEach((e) => (e ? console.log(e) : null))}
        Error: see console!
      </span>
    );
  }
 
   return (
  <>
    <ul>
      {stuff.map((el) => (
        <li key={el.id}>{el.title}</li>
      ))}
    </ul>
    <ul>
      {things.map((el) => (
        <li key={el.id}>{el.title}</li>
      ))}
    </ul>
    <ul>
      {differentStuff.map((el) => (
        <li key={el.id}>{el.title}</li>
      ))}
    </ul>
  </>
);
 }

我确信一定有更好的方法来做到这一点。出于多种原因,我对 React-Query 非常感兴趣,但一个很好的好处是减少了样板文件。然而,这种方法似乎并不比使用 useEffect 和 useState 来管理我的 api 调用好多少。我确实找到了 useQueries 钩子,但它并没有让这个真正变得更干净。

有谁知道 React-Query 中是否有一种方法可以进行多个查询并且只返回一个 isLoading、isError 和 error(array?) 响应?或者只是一种更好的方法来处理我缺少的多个查询?

reactjs react-query
5个回答
25
投票

我确实找到了 useQueries 钩子,但它并没有让这个真正变得更干净。

useQueries 为您提供一个结果数组,以便您可以映射它们:

const isLoading = queryResults.some(query => query.isLoading)

如果您有一个触发多个并发请求的组件,那么库可以做的就是降低复杂性。每个查询都可以有自己的加载状态/错误状态/数据。每个查询可以有自己的设置并且可以表现不同。推荐的方法仍然是将其提取到自定义挂钩并从中返回您想要的内容。

可以通过使用带有

useErrorBoundary
选项的错误边界来简化错误处理。为了简化加载体验,您可以尝试
suspense
(尽管是实验性的),它将显示所有查询的
fallback
加载器。

这种方法似乎并不比使用 useEffect 和 useState 来管理我的 api 调用好多少。

这忽略了所有优点,例如(除其他外)缓存、后台重新获取、突变、智能失效等。


12
投票

使用依赖查询,您可以遵循文档的示例。

const { data: user } = useQuery(['user', email], getUserByEmail)
const userId = user?.id
// Then get the user's projects

const { isIdle, data: projects } = useQuery(
  ['projects', userId],
  getProjectsByUser,
  {
    // The query will not execute until the userId exists
    enabled: !!userId,
  }
 )

官方文档-依赖查询


9
投票

有两种选择:

  • 如果查询彼此独立,请使用 useQueries 挂钩。
import React from 'react';
import { useQueries } from '@tanstack/react-query';
import axios from 'axios';

export default function Example() {
  const [postsQuery, usersQuery] = useQueries({
    queries: [
      {
        queryKey: ['posts'],
        queryFn: () =>
          axios
            .get('https://jsonplaceholder.typicode.com/posts')
            .then((res) => res.data),
      },

      {
        queryKey: ['users'],
        queryFn: () =>
          axios
            .get('https://jsonplaceholder.typicode.com/users')
            .then((res) => res.data),
      },
    ],
  });

  if (postsQuery.isLoading) return 'Loading Posts...';
  if (usersQuery.isLoading) return 'Loading Users...';

  if (postsQuery.error)
    return 'An error has occurred: ' + postsQuery.error.message;

  if (usersQuery.error)
    return 'An error has occurred: ' + usersQuery.error.message;

  return (
    <div>
      <h2>Posts</h2>
      {postsQuery.data?.map((post) => {
        return (
          <div key={post.id} style={{ display: 'flex' }}>
            <span>{post.id}-&nbsp;</span>
            <div>{post.title}</div>
          </div>
        );
      })}

      <h2>Users</h2>
      {usersQuery.data?.map((user) => {
        return (
          <div key={user.id} style={{ display: 'flex' }}>
            <span>{user.id}-&nbsp;</span>
            <div>{user.name}</div>
          </div>
        );
      })}
    </div>
  );
}

  • 或者,通过添加
    enabled
    属性,使用相关查询选项。
import React from 'react';
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';

export default function Example() {
  const {
    isLoading: loadingPost,
    error: errorPost,
    data: postData,
  } = useQuery(['post', 1], () =>
    axios
      .get('https://jsonplaceholder.typicode.com/posts/1')
      .then((res) => res.data)
  );

  const {
    isLoading: loadingPostComments,
    error: errorPostComments,
    data: commentsData,
  } = useQuery(
    ['comments', 'post', 1],
    () =>
      axios
        .get('https://jsonplaceholder.typicode.com/posts/1/comments')
        .then((res) => res.data),
    {
      enabled: postData && Object.keys(postData).length > 0,
    }
  );

  if (loadingPost) return 'Loading Posts...';
  if (errorPost) return 'An error has occurred: ' + errorPost.message;

  if (loadingPostComments) return 'Loading Comments...';
  if (errorPostComments)
    return 'An error has occurred: ' + errorPostComments.message;

  return (
    <div>
      <h2>Post</h2>
      {postData && (
        <div key={postData.id} style={{ display: 'flex' }}>
          <span>{postData.id}-&nbsp;</span>
          <div>{postData.title}</div>
        </div>
      )}

      <h2>-- Comments</h2>
      {commentsData?.map((comment) => {
        return (
          <div key={comment.id} style={{ display: 'flex' }}>
            <span>{comment.id}-&nbsp;</span>
            <div>{comment.body}</div>
          </div>
        );
      })}
    </div>
  );
}

您可以在此链接上找到更多信息。


4
投票

您可以将查询抽象为单独的查询文件,然后为要一起获取的每个数据集合创建自定义挂钩(最有可能的是呈现单个页面所需的查询集合)

// ../queries/someQuery.js
export const useContactInformation = () => {

  const { data, isLoading, error } = useQuery(CONTACT_INFORMATION, () => apicall(someparams), {
    enabled: !!user?.id,
  });

  return {
    contactInformation: data
    isLoading,
    error,
  };
};

然后在另一个文件中...

// ../hooks/somepage.js

export const useSomePageData = () => {
  const { contactInformation, error: contactError, isLoading: contactIsLoading } = useContactInformation();
  const { activityList, error: activityError, completedActivity, isLoading: activityIsLoading } = useActivityList();

  # return true, only when all queries are done
  const isLoading = activityIsLoading || contactIsLoading;
  # return true, only when all queries run successfully
  const error = activityError || contactError;

  return {
    error,
    isLoading,
    activityList,
  };
};

0
投票

利用

useQueries
是解决方案,以下是另一种使其更易于维护的方法:

const useMultipleData = () => {

const queries = useQueries([
    { queryKey: 'posts', queryFn: fetchPosts },
    { queryKey: 'comments', queryFn: fetchComments },
  ]);

  return {
    posts: queries[0].data,
    isPostsLoading: queries[0].isLoading,
    postsError: queries[0].error,
    comments: queries[1].data,
    isCommentsLoading: queries[1].isLoading,
    commentsError: queries[1].error,
  };
};

然后像这样使用它:

const {
    posts,
    isPostsLoading,
    postsError,
    comments,
    isCommentsLoading,
    commentsError,
  } = useMultipleData();
© www.soinside.com 2019 - 2024. All rights reserved.