Вольный перевод статьи Getting started with React Query - Easy server state management in React
Обработка стейта, исходящего от сервера, может действительно вызвать некоторые проблемы в React. Есть много вещей, о которых вы должны подумать при работе с асинхронными данными, таких как обновление, кэширование или повторная загрузка.
Вот тут-то и пригождается react-query. Он легко справляется с ними, а также предлагает простые решения для оптимистичного рендеринга, бесконечного скроллинга, пагинации и многого другого.
Вот небольшое демо того, что мы будем строить:
Если вы хотите перейти прямо в код, вы можете найти репозиторий здесь: https://github.com/wwebdev/react-query-demo
В этом уроке я предполагаю, что у вас установлена node. Прежде всего, создадим новое react приложение с помощью npx create-react-app. После этого установим react-query с помощью npm i --save react-query.
Для демонстрации того, как работает react-query, я буду использовать Json Placeholder API для создания простого блога.
Загрузка данных
Прежде всего, удалим весь шаблонный код в App.js и заменим его следующим кодом:
import React from 'react'; import { useQuery } from 'react-query' const getPosts = async () => { const response = await fetch('https://jsonplaceholder.typicode.com/posts') return response.json() } function App() { const { status, data, isFetching, error } = useQuery('posts', getPosts) if (status === 'loading') { return <div>loading...</div> // loading state } if (status === 'error') { return <div>{error.message}</div> // error state } return ( <div> { data && <ul>{ data .slice(0,10) // only take frist 10 for now // render list of titles .map(d => <li key={`post-${d.id}`}>{d.title}</li>) }</ul> } { isFetching && <p>updating...</p> } </div> ) } export default App Сначала определили функцию getPosts - она может включать в себя все, что угодно, если она возвращает асинхронную функцию.
В начале App() вызывается хук useQuery с идентификатором для данных, которые извлекаются, и с асинхронной функции getPosts.
Хук возвращает status, data, isFetching и error. Это довольно хорошо описывает себя. Статус может быть «success», «loading» или «error». Остальная часть компонента отображает результат в трех возможных состояниях.
Внутренние компоненты react-query теперь позаботятся обо всей логике кэширования и обновления. Это означает, что всякий раз, когда вы переходите на страницу, вы будете знать, что отображаемые данные будут там мгновенно, если вы предварительно извлекли их, и они всегда будут в актуальном серверном состоянии.
Это в принципе все, что вам нужно знать, чтобы начать использовать react-query. Но давайте расширим этот пример, чтобы увидеть кэширование и обновление в действии!
Расширение приложения
Прежде всего, я перенесу код из App.js в новый компонент components/Home.js. Далее я переименую компонент и добавлю NavLink в список сообщений.
import React from 'react' import { NavLink } from 'react-router-dom' import { useQuery } from 'react-query' const getPosts = async () => { const response = await fetch('https://jsonplaceholder.typicode.com/posts') await new Promise(r => setTimeout(r, 1000)) // wait a second return response.json() }; function Home() { const { status, data, isFetching, error } = useQuery('posts', getPosts) if (status === 'loading') { return <div>loading...</div> // loading state } if (status === 'error') { return <div>{error.message}</div> // error state } return ( <div> { data && <ul>{ data .slice(0,10) // only take frist 10 for now .map(d => <li key={`post-${d.id}`}> <NavLink to={`/post/${d.id}`}>{d.title}</NavLink> </li>) // render list of titles }</ul> } { isFetching && <p>updating...</p> } </div> ); } export default Home Теперь давайте добавим в App.js маршрутизатор, который сопоставляет маршруты / для Home.js и /post/:id для одной страницы поста.
import React from 'react' import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' import Home from './components/Home' import Post from './components/Post' function App() { return ( <Router> <Switch> <Route exact path="/" component={Home}/> <Route path = '/post/:id' render = {routerProps => <Post id={routerProps.match.params.id}/>} /> </Switch> </Router> ) } export default App И наконец, я создам новый компонент components/Post.js для отображения данных одного поста. Объяснение будет следовать после кода.
import React from 'react' import { NavLink } from 'react-router-dom' import { useQuery } from 'react-query' const Post = ({ id }) => { const getPost = async () => { const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`) const jsonResponse = await response.json() jsonResponse.title = `${jsonResponse.title} - ${Math.random().toString(36)}` await new Promise(r => setTimeout(r, 1000)) // wait a second return jsonResponse } const { status, data, isFetching } = useQuery(`post-${id}`, getPost) if (status === 'loading') { return <div>loading...</div> // loading state } return ( <div> <h1>{data.title}</h1> <p>{data.body}</p> { isFetching && <p>updating...</p> } <br /> <NavLink to="/">Home</NavLink> </div> ) } export default Post Таким образом, useQuery здесь не сильно отличается от того, что в Home.js. Он добавляет id к идентификатору, поэтому каждое сообщение имеет свой собственный state. Кроме того, я добавил тайм-аут на 1 секунду к функцию getPost, чтобы сделать состояние загрузки более заметным. Также я добавил случайную строку к заголовку, чтобы сделать повторную загрузку видимой.
И это на самом деле весь код GIF, которую вы видели в начале поста.
Если вы начнете работать с react-query, я бы порекомендовал вам посмотреть react-query-devtools, чтобы иметь возможность просматривать state и кэш.

Top comments (4)
Я использовал redux много раз для достаточно больших проектов. Как вы думаете, подходит ли React-query для больших проектов?
Вполне! Тут идёт работа с запросами. Поэтому, чем больше запросов закешировно, тем лучше)
Сами тоже в своем проекте двигаемся на пути к переходу к react-query
Ага, спасибо за ответ.
Вообще reaqt-query это обвязка которая пытается привнести graphql инструментарий для проектов с rest. Возможно, вам лучше сразу попытаться использовать graphql.