Как получить n зависимых данных с помощью React-query

Мне нужна помощь с библиотекой response-query. Я получаю список объектов, а затем для всех, кто мне нужен, получаю дополнительные данные из другой службы. Как мне сделать это с помощью хука response-query useQuery?

Пример 1:

const { data} = useQuery("USERS", fetchUsers);
data.map(user => useQuery(["ACCOUNT", {userId: user.id}], 
                 ({userId}) => fetchAccount(userId)));

Ошибка:

ESLint: React Hook useQuery нельзя вызвать внутри обратного вызова. React Hooks необходимо вызывать в компоненте функции React или в пользовательской функции React Hook. (перехватчики реакции / перехватчики правил)


person Michał Mielec    schedule 17.02.2020    source источник


Ответы (4)


Я нашел ответ, предоставленный автором response-query:

Из того, что вы описали, похоже, что это классический вариант: я хочу использовать перехватчики React в режиме массива, в котором мы все определенно были раньше.

В 99,9% случаев я чувствую, что ответ на это затруднительное положение состоит в том, чтобы отрендерить использование хуков в несколько компонентов и скомпоновать их результаты после рендеринга, но здесь определенно есть крайние случаи и множество компромиссов. Однако это лучшее решение, которое у нас есть на данный момент, поскольку нет отличного способа использовать MapHooks (item = ›useQuery (item), items). Это было бы действительно здорово, но поверьте мне, я пошел по этому пути, и хотя у меня действительно кое-что работало более или менее, это в конечном итоге усложнило мой код намного больше, чем я ожидал. В настоящее время React Query, скорее всего, не получит ту функцию, о которой вы говорите, поскольку сама библиотека использует хуки, она привязана к той же механике, что и вы, и потребует огромного количества переархивирования, чтобы даже приблизиться к тому, что вы ' повторное описание. Окупаемость инвестиций не окупится и, скорее всего, снизит производительность, размер и сложность API для остальной части библиотеки.

Чтобы решить вашу конкретную проблему на сегодня, я бы предложил три возможных решения:

  1. Ничего страшного, если запрашивается информация о нескольких столбцах, даже если вам нужен только один. Отрегулируйте запоминание / потребление этой информации в столбцах, чтобы обновлять только в том случае, если они новые. Это значительно упростит задачу и, вероятно, позволит вам двигаться вперед быстрее.

  2. Составьте запросы сборщика информации о столбцах в несколько компонентов, которые ничего не отображают, и вместо этого передайте им опору, которая позволяет им сообщать свои результаты обратно родительскому компоненту, а затем объединять их результаты в родительском компоненте в один массив столбцов.

  3. Создайте свой собственный небольшой кеш для хранения списка уже извлеченной информации о столбцах, а затем объедините логику извлечения в один запрос. Используйте свой настраиваемый объект кеша, чтобы заполнять слоты в этом массиве, когда данные доступны, а также поддерживать их в актуальном состоянии, очищая кеш так часто, как вы сочтете нужным. Я бы рассмотрел это в крайнем случае, так как вы, по сути, начнете управлять своим собственным кешем, а это именно то, что React Query предназначен для вас. Если вы это сделаете, я бы посоветовал взвесить преимущества, которые вы получаете с React Query, здесь в целом. Это может быть крайний случай, когда стратегия кэширования / выборки React Query не подходит, и это нормально.

Надеюсь это поможет!

https://github.com/tannerlinsley/react-query/issues/138#issuecomment-589709270

person Michał Mielec    schedule 21.02.2020

Используйте зависимые запросы, - пока не будет разрешено data (пользователи), ваша вторая строка ключа запроса должна быть ложной, пока у вас не появятся пользователи:

// rename `data` to `users`
const { data: users } = useQuery("USERS", fetchUsers);

// `users` causes query key to be falsy until they are loaded
// rename `data` to `accounts`, map `users` id only and pass to pass in as param
const { data: accounts } = useQuery([users && "ACCOUNT", users.map((u) => u.id)], async (key, userIds) => {
  const response = await fetchAccounts(userIds);

  // TODO any mapping?

  return response;
});

https://www.npmjs.com/package/react-query#dependent-queries

person ilovett    schedule 22.05.2020
comment
Это не мой случай :) В моем примере я не получаю сразу все учетные записи. Я хотел бы получать учетные записи по одному и кэшировать их по одному в отдельном кеш-ключе для каждого пользователя. - person Michał Mielec; 30.05.2020
comment
Обновленная ссылка: react-query.tanstack.com/docs/guides/queries # зависимых-запросов - person EricM; 07.09.2020
comment
@ MichałMielec У меня такая же проблема, вы узнали, как это сделать? - person shaxaaa; 15.09.2020
comment
@shaxaaa Я разместил объяснение, данное автором библиотеки. Короче не возможно. stackoverflow.com/a/60342970/4809490 - person Michał Mielec; 16.09.2020
comment
Обновленная ссылка: react-query.tanstack.com/guides/dependent-queries - person Dan Hooper; 19.03.2021

Это похоже на текущее решение, по крайней мере, для ответа на запрос v3:

 // Get the user
 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,
   }
 )

 // isIdle will be `true` until `enabled` is true and the query begins to fetch.
 // It will then go to the `isLoading` stage and hopefully the `isSuccess` stage :)

Из документа по зависимым запросам.

person Artur Carvalho    schedule 17.02.2021

Я начал использовать для этого другую технику, когда я выполняю оба вызова из функции get. Преимущество заключается в том, что за функцией скрывается сложность API. Недостатком является менее детальный контроль над компонентом.

Ниже приведен пример (не проверенный), похожий на то, что я обычно делаю.

 const getProjectsByUser = () => {
   // I'm using axios, but you can use fetch or something else.
   const {data} = axios.get("/api/user")
   
   // Let's assume the user endpoint returns a user with an array of projects
   const projectNames = data.user.projects

   const fetchProjects = projecNames.map(name => axios.get(`/api/projects/${name}`)
   
   // I may use Promise.all or Promise.allSettled
   const projResponses = await Promise.all(fetchProjects)

   return projResponses.data;
 }

Единственный код на каком-то компоненте:

 import {getProjectsByUser } from "some path"

 const { data: user } = useQuery(['user', email], getProjectsByUser)
 const userId = user?.id
 
person Artur Carvalho    schedule 14.07.2021