React-Query делает ваше управление состоянием более элегантным

React.js
React-Query делает ваше управление состоянием более элегантным

Ниже приведен простой случай получения данных с сервера.

function App() {

  const [data, updateData] = useState(null);
  const [isError, setError] = useState(false);
  const [isLoading, setLoading] = useState(false);
  
  useEffect(async () => {
    setError(false);
    setLoading(true);
    try {
      const data = await axios.get('/api/user');
      updateData(data);
    } catch(e) {
      setError(true);
    }
    setLoading(false);
  }, [])

}

При написании компонентов с помощью React Hooks нам часто приходится вручную поддерживать состояние обработки с сервера. Вызывает некоторые проблемы в повседневном развитии. При работе с асинхронными данными нам нужно учитывать множество вещей, таких как обновление, кэширование или повторная выборка. Использование React-Query может помочь вам более эффективно управлять состоянием на стороне сервера.

Что такое React-запрос

Это библиотека запросов для React Hooks. Эта библиотека поможет вам получать, синхронизировать, обновлять и кэшировать ваши удаленные данные. Она предоставляет два простых хука для выполнения таких операций, как добавление, удаление, изменение и запрос. React-Query использует декларативное управление состоянием сервера и может использовать нулевую настройку из поле используется для обработки кэширования, фоновых обновлений и устаревших данных. Никакой громоздкой конфигурации, напишите useReduce и поддерживайте глобальное состояние, просто знайте, как использовать Promise или async/await, передать функцию, которая может анализировать данные (или вызвать ошибку), и оставить все остальное React-Query, используя меньше кода для более высокой эффективности.

Начать работу с React-Query

Установить React-запрос

 $ npm i react-query
 # or
 $ yarn add react-query

Давайте перепишем его с помощью React-Query


 import { useQuery } from 'react-query'
 
 function App() {

   const {data, isLoading, isError} = useQuery('userData', () => axios.get('/api/user'));
   
   if (isLoading) {
     return <div>loading</div>;
   }
   
   return (
     <ul>
       {data.map(user => <li key={user.id}>{user.name}</li>)}
     </ul>
   )
 }

Строка «userData» в примере является уникальным ключом для этого запроса. Как видите, React-Query инкапсулирует полное промежуточное состояние запроса (isLoading, isError...). Мало того, React-Query также делает за нас следующую работу:

  1. Выдается только один запрос, когда несколько компонентов запрашивают один и тот же запрос
  2. Стратегия аннулирования/обновления данных кеша (определение того, что кеш подходит для аннулирования, и автоматический запрос данных после аннулирования)
  3. Очистить мусор от устаревших данных

Три важных знания в React-Query

Общая конфигурация параметров

  • staleTime Интервал времени для повторной выборки данных, по умолчанию 0
  • Cache Cache Cache Time По умолчанию 1000 60 5 5 минут
  • Повторите попытку повторной попытки по умолчанию 3 раза
  • refetchOnWindowFocus Обновлять данные, когда окно восстанавливает фокус. По умолчанию false.
  • refetchOnReconnect повторное подключение к сети
  • Перемонтировать экземпляр refetchOnMount
  • Если включено значение «false», «useQuery» не будет запущен, и для запуска операции необходимо использовать возвращаемый им «refetch».

Как настроить глобально? следующим образом:

import { ReactQueryConfigProvider, ReactQueryProviderConfig } from 'react-query';

const queryConfig: ReactQueryProviderConfig = {
  /**
   * refetchOnWindowFocus 窗口获得焦点时重新获取数据
   * staleTime 过多久重新获取服务端数据
   * cacheTime 数据缓存时间 默认是 5 * 60 * 1000 5分钟
   */
  queries: { 
    refetchOnWindowFocus: true,
    staleTime: 5 * 60 * 1000, 
    retry: 0
  },
};

ReactDOM.render(
    <ReactQueryConfigProvider config={queryConfig}>
        <App />
    </ReactQueryConfigProvider>
    document.getElementById('root')
  );
也可以单独配置,如下:

function Todos() {
   // 第三个参数即可传参了
   // "enabled"参数为false的化,不会自动发起请求,而是需要调用“refetch”来触发
   const {
     isIdle,
     isLoading,
     isError,
     data,
     error,
     refetch,
     isFetching,
   } = useQuery('todos', fetchTodoList, {
     enabled: false,
   })
 
   return (
     <>
       <button onClick={() => refetch()}>Fetch Todos</button>
 
       {isIdle ? (
         'Not ready...'
       ) : isLoading ? (
         <span>Loading...</span>
       ) : isError ? (
         <span>Error: {error.message}</span>
       ) : (
         <>
           <ul>
             {data.map(todo => (
               <li key={todo.id}>{todo.title}</li>
             ))}
           </ul>
           <div>{isFetching ? 'Fetching...' : null}</div>
         </>
       )}
     </>
   )
 }

Запрос данных и работа

useQuery (проверить) запросить данные (получить)

Основное использование

function Todos() {
   // useQuery的第一个参数,作为useQuery查询的唯一标识,该值唯一
   // 可以是string、array、object
   // string -> useQuery('todos', ...) queryKey === ['todos']
   // array -> useQuery(['todo', 5], ...) queryKey === ['todo', 5]
   // object -> useQuery(['todo', 5, { preview: true }], ...)  queryKey === ['todo', 5, { preview: true }]
   const { isLoading, isError, data, error } = useQuery('todos', fetchTodoList)

   if (isLoading) {
     return <span>Loading...</span>
   }

   if (isError) {
     return <span>Error: {error.message}</span>
   }

   // also status === 'success', but "else" logic works, too
   return (
     <ul>
       {data.map(todo => (
         <li key={todo.id}>{todo.title}</li>
       ))}
     </ul>
   )
 }

передать параметры

function Todos({ completed }) {
    // useQuery(['todo', { status: 1, page: 1 }], ...)  queryKey === ['todo', { status: 1, page: 1 }]
    // 传递参数给“fetchTodoList”使用
   const queryInfo = useQuery(['todos', { status: 1, page: 1 }], fetchTodoList)
 }

 // 函数参数
 // key -> “todos”
 // status -> 1 page -> 1
 function fetchTodoList(key, { status, page }) {
   return new Promise()
   // ...
 }

Библиотека также реализует часто используемые операции запросов:

useMutation (добавить, изменить, удалить) данные операции (Post, Delete, Patch, Put

// 当“mutate()”被调用时,执行“pingMutation”
const PingPong = () => {
   const [mutate, { status, data, error }] = useMutation(pingMutation)

   const onPing = async () => {
     try {
       const data = await mutate()
       console.log(data)
     } catch {
     }
   }
   return <button onClick={onPing}>Ping</button>
 }

передать параметры

// "mutate({title})"就会将参数“title”传递给“createTodo”函数了
const createTodo = ({ title }) => {
  console.log("title ", title)
}

const CreateTodo = () => {
const [title, setTitle] = useState('')
const [mutate] = useMutation(createTodo)

const onCreateTodo = async e => {
    e.preventDefault()

    try {
    await mutate({ title })
    // Todo was successfully created
    } catch (error) {
    // Uh oh, something went wrong
    }
}

return (
    <form onSubmit={onCreateTodo}>
    <input
        type="text"
        value={title}
        onChange={e => setTitle(e.target.value)}
    />
    <br />
    <button type="submit">Create Todo</button>
    </form>
)
}

очистить кэш

Всякий раз, когда мы заканчиваем редактирование статьи и возвращаемся на страницу со списком, если мы не очищаем кеш, данные все еще остаются в кеше, поэтому нам нужно очистить кеш, чтобы сделать «userQuery» недействительным, и повторно получить последние данные, когда мы возвращаемся на страницу списка

Код ссылки выглядит следующим образом

import { useMutation, useQueryCache } from 'react-query'

const queryCache = useQueryCache()

const [mutate] = useMutation(addTodo, {
    onSuccess: () => {
        // invalidateQueries 的匹配规则
        // eg:
        // queryCache.invalidateQueries('todos') 那么如下两个`query key`都会被匹配到,匹配到的缓存都会失效
        // const todoListQuery = useQuery('todos', fetchTodoList)
        // const todoListQuery = useQuery(['todos', { page: 1 }], fetchTodoList)
        queryCache.invalidateQueries('todos')
        queryCache.invalidateQueries('reminders')
    },
})

Devtools, поддерживающие инструменты разработки

Импорт инструментов разработки

import { ReactQueryDevtools } from 'react-query/devtools'

По умолчанию Devtools включен, когда process.env.NODE ENV === 'production' , не беспокойтесь об их исключении при сборке.

При включении в плавающем режиме он установит инструменты разработки как фиксированный плавающий элемент в разработанном приложении и предоставит кнопку-переключатель в углу экрана для отображения и скрытия инструментов разработки.

По возможности добавляйте следующий код в свое приложение React. Чем ближе к корню страницы, тем лучше!

 import { ReactQueryDevtools } from 'react-query/devtools'
 
 function App() {
   return (
     <QueryClientProvider client={queryClient}>
       {/* The rest of your application */}
       <ReactQueryDevtools initialIsOpen={false} />
     </QueryClientProvider>
   )
 }

Экология вокруг React-Query

Окружающая среда React-Query отвечает ежедневным потребностям разработки и поддерживает TypeScript и GraphQL. Его можно использовать в React Native, а также он совместим со схемами рендеринга на стороне сервера.SSR поддерживает использование Next.js.

Сравнение других планов

  1. react-query может использовать хуки для мутации, поддерживает больше параметров (например, keepPreviousData), имеет больше функций и больше подходит для проектов со сложными API.
  2. swr относительно легкий и может использоваться где угодно без установки Provider в родительском компоненте. легче

Сравнительная таблица схем, связанных с официальным сайтом

React Query SWR (Website) Apollo Client (Website) RTK-Query (Website)
Github Repo / Stars 18K 16K 16K 372
Platform Requirements React React React, GraphQL Redux
Their Comparison (none) (none) Comparison
Supported Query Syntax Promise, REST, GraphQL Promise, REST, GraphQL GraphQL Promise, REST, GraphQL
Supported Frameworks React React React + Others Any
Supported Query Keys JSON JSON GraphQL Query JSON
Query Key Change Detection Deep Compare (Stable Serialization) Referential Equality (===) Deep Compare (Unstable Serialization) Referential Equality (===)
Query Data Memoization Level Query + Structural Sharing Query Query + Entity + Structural Sharing Query
Bundle Size 11.2KB 5.0KB 33.9KB 10.3KB
API Definition On-Use, Declarative On-Use GraphQL Schema Declarative
Queries
Caching
Devtools 🟡
Polling/Intervals
Parallel Queries
Dependent Queries
Paginated Queries
Infinite Queries 🛑
Bi-directional Infinite Queries 🔶 🔶 🛑
Infinite Query Refetching 🛑 🛑
Lagged Query Data1 🛑 🛑
Selectors 🛑
Initial Data
Scroll Recovery
Cache Manipulation
Outdated Query Dismissal
Render Optimization2 🛑 🛑
Auto Garbage Collection 🛑 🛑
Mutation Hooks 🟡
Offline Mutation Support 🛑 🟡 🛑
Prefetching APIs 🔶
Query Cancellation 🛑 🛑 🛑
Partial Query Matching3 🛑 🛑
Stale While Revalidate 🛑
Stale Time Configuration 🛑 🛑 🛑
Pre-usage Query/Mutation Configuration4 🛑 🛑
Window Focus Refetching 🛑 🛑
Network Status Refetching 🛑
General Cache Dehydration/Rehydration 🛑
Offline Caching ✅ (Экспериментальный) 🛑 🔶
React Suspense (Experimental) 🛑 🛑
Abstracted/Agnostic Core 🛑
Automatic Refetch after Mutation5 🔶 🔶
Normalized Caching6 🛑 🛑 🛑

узнать больше

Суммировать

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

Ссылка на ссылку:

Официальный сайт React-Query

Решите половину своих проблем управления состоянием с помощью реагирующего запроса

react-query