React-Query: как использоватьQuery при нажатии кнопки
Я новичок в этой библиотеке запросов-ответов.
Я знаю, что когда я хочу получить данные, с помощью этой библиотеки я могу сделать что-то вроде этого:
const fetchData = async()=>{...}
// it starts fetching data from backend with this line of code
const {status, data, error} = useQuery(myKey, fetchData());
Оно работает. Но как запустить выборку данных только при нажатии кнопки?, Я знаю, что мог бы сделать что-то вроде<Button onPress={() => {useQuery(myKey, fetchData())}}/>
, но как управлять возвращенными данными и статусом...
11 ответов
Согласно Справочнику API, вам необходимо изменитьenabled
для параметра false, чтобы отключить автоматический запуск запроса. Потом перекачиваешь вручную.
// emulates axios/fetch since useQuery expectes a Promise
const emulateFetch = async _ => {
return new Promise(resolve => {
resolve([{ data: "ok" }]);
});
};
const handleClick = () => {
// manually refetch
refetch();
};
const { isLoading, error, data, refetch } = useQuery("key", emulateFetch, {
refetchOnWindowFocus: false,
enabled: false // turned off by default, manual refetch is needed
});
return (
<div>
<button onClick={handleClick}>Click me</button>
{JSON.stringify(data)}
</div>
);
Работа песочницы здесь.
Â
Отличная вещь о enabled
вариант состоит в том, что вы передаете все, что возвращает логическое значение. Таким образом вы можете создавать зависимые / последовательные запросы.
// Get the user
const { data: user } = useQuery(['user', email], getUserByEmail)
// Then get the user's projects
const { isIdle, data: projects } = useQuery(
['projects', user.id],
getProjectsByUser,
{
// `user` would be `null` at first (falsy),
// so the query will not execute until the user exists
enabled: user,
}
)
Вы должны пройти manual: true
параметр, чтобы запрос не загружался при монтировании. Также вы должны пройтиfetchData
без скобок, поэтому вы передаете ссылку на функцию, а не значение. Для вызова запроса вы используете refetch().
const {status, data, error, refetch} = useQuery(myKey, fetchData, {
manual: true,
});
const onClick = () => { refetch() }
Обратитесь к разделу ручных запросов в документации по реакциям для получения дополнительной информации https://github.com/tannerlinsley/react-query
Похоже, что документация изменилась и сейчас отсутствует раздел ручных запросов. Однако, глядя на useQuery API, вам, вероятно, потребуется установитьenabled
в false, а затем использовать refetch
для ручного запроса при нажатии кнопки. Вы также можете использоватьforce: true
чтобы он запрашивал независимо от свежести данных.
Есть две проблемы сrefetch()
:
- он отправит запрос на сервер, даже если ответ уже кэширован и
staleTime
не истек срок действия - вы не можете передать ему параметры
Если вам нужно сделать запрос по такому событию, какonClick
илиonChange
, использоватьqueryClient.fetchQuery()
.
function MyComponent() {
const queryClient = useQueryClient();
const handleOnClick = async () => {
const data = await queryClient.fetchQuery({ queryKey, queryFn });
}
return <button onClick={handleOnClick}>My Button</button>;
}
Вы можете видеть, что создатель и текущий сопровождающий рекомендуют этот подход.
Сначала реагирующий запрос дает нам включенную опцию, и по умолчанию это правда
const fetchData = async()=>{...}
const {status, data, error , refetch} = useQuery(myKey, fetchData() , {
enabled : false
}
);
<button onClick={() => refetch()}>Refetch</button>
Вы можете попробовать эту версию:
const fetchData = async()=>{...}
// it starts fetching data from backend with this line of code
const {status, data, error, refetch } = useQuery(
myKey,
fetchData(),
{
enabled: false,
}
);
const onClick = () => { refetch() }
// then use onClick where you need it
Из документации Doc:
enabled: boolean
- Установите значение false, чтобы отключить автоматический запуск этого запроса.
- Может использоваться для зависимых запросов.
refetch: (options: { throwOnError: boolean, cancelRefetch: boolean }) => Promise<UseQueryResult>
- Функция для повторной загрузки запроса вручную.
- В случае ошибок запроса, ошибка будет только записана. Если вы хотите, чтобы возникла ошибка, передайте
throwOnError: true option
- Если
cancelRefetch
являетсяtrue
, то текущий запрос будет отменен до того, как будет сделан новый запрос.
Есть еще один способ сделать это, который также работает, если вы хотите запускать несколько повторений.
const [fetch, setFetch] = useState(null);
const query = useQuery(["endpoint", fetch], fetchData);
const refetch = () => setFetech(Date.now());
// call the refetch when handling click.
Если вы хотите обновить несколько сущностей, у вас может быть useState верхнего уровня, который вызывается, например, fetchAll и:
...
const query = useQuery(["endpoint", fetch, fetchAll], fetchData);
...
и этот код также сработает, если вы нажмете кнопку, чтобы получить все.
Если ключ тот же, используйте refetch(), если ключ другой, используйте useState для запуска запроса.
Например:
const [productId, setProductId] = useState<string>('')
const {status, data, error, refetch} = useQuery(productId, fetchData, {
enable: !!productId,
});
const onClick = (id) => {
if(productId === id) {
refetch()
}
else {
setProductId(id)
}
}
В ответном запросе есть возможность useQuery() запускать перехват, когда вы захотите. Опция включена и принимает только логическое значение. Вот пример:
const useGetAllUsers = useQuery({
queryKey:["users"],
queryFn: your_fetch_request,
enabled: true|false
})
здесь вы можете передать любое логическое состояние из этого компонента.
Как упоминалось другими в этой теме, использование делает запрос, даже если кеш не устарел. Поэтому в идеале мы бы не звонилиquery.refetch
только еслиquery.isFetched && !query.isStale
. Я хотел инкапсулировать эту логику внутри хука, и вот как я это решил:
// useGetFoo.ts
export const useGetFoo = () => {
const query = useQuery({
queryKey: ["foo"],
queryFn: fetcher,
enabled: false,
});
const fetch = (): ReturnType<typeof query.refetch> => {
if (query.isFetching) return Promise.resolve(query);
if (query.isFetched && !query.isStale && !query.isError)
// browser hangs without this setTimeout
return new Promise((resolve) => setTimeout(() => resolve(query)));
return query.refetch();
};
return { ...query, fetch };
};
И тогда мы можем использовать его следующим образом:
// SomeComponent.tsx
const { data, fetch: getFoo } = useGetFoo();
useEffect(() => {
void getFoo();
}, [getFoo]);
вы можете использовать useLazyQuery()
import React from 'react';
import { useLazyQuery } from '@apollo/client';
function DelayedQuery() {
const [getDog, { loading, error, data }] = useLazyQuery(GET_DOG_PHOTO);
if (loading) return <p>Loading ...</p>;
if (error) return `Error! ${error}`;
return (
<div>
{data?.dog && <img src={data.dog.displayImage} />}
<button onClick={() => getDog({ variables: { breed: 'bulldog' } })}>Click me!</button>
</div>
);
}
ссылка: https://www.apollographql.com/docs/react/data/queries/#manual-execution-with-uselazyquery