DEV Community

Arif Balaev
Arif Balaev

Posted on

6 1

React Query - Простое управление серверным стейтом в React

Вольный перевод статьи 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
Enter fullscreen mode Exit fullscreen mode

Сначала определили функцию 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
Enter fullscreen mode Exit fullscreen mode

Теперь давайте добавим в 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
Enter fullscreen mode Exit fullscreen mode

И наконец, я создам новый компонент 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
Enter fullscreen mode Exit fullscreen mode

Таким образом, useQuery здесь не сильно отличается от того, что в Home.js. Он добавляет id к идентификатору, поэтому каждое сообщение имеет свой собственный state. Кроме того, я добавил тайм-аут на 1 секунду к функцию getPost, чтобы сделать состояние загрузки более заметным. Также я добавил случайную строку к заголовку, чтобы сделать повторную загрузку видимой.

И это на самом деле весь код GIF, которую вы видели в начале поста.

Если вы начнете работать с react-query, я бы порекомендовал вам посмотреть react-query-devtools, чтобы иметь возможность просматривать state и кэш.

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

Top comments (4)

Collapse
 
akhalinem profile image
Abror Khalilov

Я использовал redux много раз для достаточно больших проектов. Как вы думаете, подходит ли React-query для больших проектов?

Collapse
 
balaevarif profile image
Arif Balaev

Вполне! Тут идёт работа с запросами. Поэтому, чем больше запросов закешировно, тем лучше)
Сами тоже в своем проекте двигаемся на пути к переходу к react-query

Collapse
 
akhalinem profile image
Abror Khalilov

Ага, спасибо за ответ.

Collapse
 
vkhv profile image
Vladislav Khvostov

Вообще reaqt-query это обвязка которая пытается привнести graphql инструментарий для проектов с rest. Возможно, вам лучше сразу попытаться использовать graphql.

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up