你不会还在useEffect中请求数据吧

使用React Query代替useEffect获取数据的优势与对比

在构建现代React应用时,我们经常需要从后端API获取数据来渲染界面。传统的方式是使用React的useEffect钩子结合fetchaxios等HTTP请求库来完成数据获取和状态管理。然而,随着React Query的出现,获取和同步服务器状态的方式得到了显著的改进。本文将详细介绍使用React Query代替useEffect获取数据的原因,并通过示例对比两种方式在代码层面的不同,在最后总结React Query的优势。

传统方式:使用useEffect获取数据

在没有使用React Query之前,我们通常会这样获取数据:

jsx 复制代码
import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true);
      setError(null);
      try {
        const response = await fetch('https://my-api/data');
        const result = await response.json();
        setData(result);
      } catch (error) {
        setError(error);
      }
      setIsLoading(false);
    };

    fetchData();
  }, []);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  return <div>{JSON.stringify(data)}</div>;
}

这段代码虽然能工作,但存在几个问题:缺乏缓存策略、复杂的错误处理、不自动的数据更新、重复的数据请求等。

使用React Query改进数据获取

接下来,看看React Query如何为我们解决上述问题和简化代码:

jsx 复制代码
import React from 'react';
import { useQuery } from 'react-query';

async function fetchData() {
  const response = await fetch('https://my-api/data');
  return response.json();
}

function MyComponent() {
  const { data, isLoading, error } = useQuery('data', fetchData);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  return <div>{JSON.stringify(data)}</div>;
}

在这个改进后的版本中,我们用useQuery钩子来代理数据加载。这行代码做了很多工作:它自动进行数据请求,处理加载状态和错误状态,还负责缓存和更新数据。

使用React Query的原因

简化的状态管理

React Query内部处理了数据的加载(isLoading)、数据更新(isFetching)、错误(error)状态管理,这使得开发者无需手动设置这些状态。

自动化的数据缓存和无效化

React Query还提供了出色的数据缓存策略。默认情况下,当组件卸载再重新挂载时,React Query会使用旧的缓存数据,同时在后台静默地为你刷新数据,保证数据的新鲜度。

更好的错误和重试处理

通过对错误状态的内部管理,React Query提供了错误捕获的机制并允许自动重试功能。这比手动实现要简单得多。

优化请求节省带宽

React Query会自动去重和合并并发的查询请求,减少不必要的网络请求,节省宽带。

React Query的优势

总结来说,React Query的主要优势包括:

  • 自动化:管理请求生命周期(查询、缓存、更新、重试)无需手动编写代码。
  • 减少样板代码:少写很多状态处理的逻辑,让代码简洁易维护。
  • 性能提升:智能缓存和数据更新策略,更少的重新渲染。
  • 鲁棒性:更健壮的错误处理和重试逻辑。
  • 开箱即用:丰富的功能如后台获取、分页、无限加载等。

在创建现代化的React应用程序时,React Query提供了一种更智能、更高效和简单的方法来处理数据获取和同步,这也是越来越多的React开发者选择它的原因。

React Query

下面将详细介绍React Query的功能,以及它如何在一个实际的场景中被使用。我们将构建一个用户列表的应用,这个应用将展示用户数据、支持数据刷新、加载更多用户以及处理错误重试。

项目准备

首先,确保已经在React项目中安装了React Query:

bash 复制代码
npm install react-query

或者

bash 复制代码
yarn add react-query

功能概览

  • 数据获取 (useQuery): 用于获取数据并提供状态管理,比如loading, error, data。
  • 缓存与背景更新 (staleTimecacheTime): 确定数据保持新鲜的时间,以及未被使用时保持在缓存中的时间。
  • 自动重试 (retry): 当请求失败时,自动进行重试。
  • 分页和加载更多 (页码或游标): 当我们需要分页或者无限加载数据时使用。
  • 数据预加载 (queryClient.prefetchQuery): 加载关键数据以提升用户体验。
  • 数据变异 (useMutation): 提交数据至服务器,并更新本地缓存。

示例应用

获取用户列表

我们使用useQuery钩子来获取用户数据。这个钩子会自动发起请求并监听数据状态。

jsx 复制代码
import { useQuery } from 'react-query';

const fetchUsers = async (page = 0) => {
  const response = await fetch(`https://my-api/users?page=${page}`);
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
};

function Users() {
  const { data, error, isLoading, isFetching } = useQuery('users', fetchUsers);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <>
      {data.users.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
      {isFetching ? <span>Updating...</span> : null}
    </>
  );
}

自动刷新和背景更新

React Query可以配置数据自动刷新的时间,我们可以设置staleTime来避免不必要的后台更新,同时让我们的数据保持最新。

jsx 复制代码
const { data } = useQuery('users', fetchUsers, {
  staleTime: 5 * 60 * 1000 // 每5分钟更新一次数据
});

自动重试

如果请求失败,React Query可以自动尝试重新获取数据:

jsx 复制代码
const { data } = useQuery('users', fetchUsers, {
  retry: 2 // 请求失败会尝试2次重试
});

分页和加载更多

对于需要加载更多数据的情况,我们可以使用React Query的页码或游标方法来实现:

jsx 复制代码
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery(
  'users',
  ({ pageParam = 0 }) => fetchUsers(pageParam),
  {
    getNextPageParam: (lastPage, allPages) => lastPage.nextPage,
  }
);

// ...

<button onClick={() => fetchNextPage()} disabled={!hasNextPage}>
  Load More
</button>

加载更多的按钮会根据hasNextPage来判断是否还有更多数据可以加载。

数据预加载

我们可以在用户的鼠标悬浮到某个按钮上时提前获取数据:

jsx 复制代码
const queryClient = useQueryClient();

// ...

<button
  onMouseEnter={() => queryClient.prefetchQuery('more-users-data', fetchAdditionalUsers)}
>
  Show More Users
</button>

数据变异

当需要提交数据到服务端时,我们可以使用useMutation来处理:

jsx 复制代码
import { useMutation, useQueryClient } from 'react-query';

const addUser = async (newUser) => {
  const response = await fetch(`https://my-api/users`, {
    method: 'POST',
    body: JSON.stringify(newUser)
  });
  if (!response.ok) {
    throw new Error('Could not add user');
  }
  return response.json();
};

function AddUser() {
  const queryClient = useQueryClient();
  const mutation = useMutation(addUser, {
    onSuccess: () => {
      // Invalidate and refetch
      queryClient.invalidateQueries('users');
    }
  });
  
  return (
    <button
      onClick={() => {
        const newUser = { name: 'New User' };
        mutation.mutate(newUser);
      }}
    >
      Add User
    </button>
  );
}

当我们向服务端增加一个新用户时,使用useMutation并提供一个成功的回调,该回调通过调用queryClient.invalidateQueries来标记用户列表的缓存为无效,以便它可以自动重新获取最新的用户列表。

总结React Query的优势

通过上述示例,我们可以看到React Query提供了强大且灵活的功能来处理数据的获取、缓存、更新、预加载、变异等操作。它大大简化了数据同步和状态管理的复杂性,使开发者可以专注于构建交互式的用户界面,而不必担心数据操作的底层细节。此外,React Query的自动重试和智能缓存策略可以提高应用的健壮性和用户的体验。

最后,简要地复习一下React Query的优势:

  1. 内置缓存功能:React Query 为获取的数据提供缓存机制,这意味着当组件重新渲染或者同用户交互时,相同的数据正在加载,不需要再次发起网络请求,可以直接从缓存中获取数据。这减少了不必要的网络请求,提高了应用的效率。

  2. 错误处理和错误重试:在处理异常数据时,错误处理和错误和错误重试在其他较繁琐。React Query 提供了强化的方式来处理这些状态,简化了开发者的工作。

  3. 优化数据获取:React Query 会自动合并重复的查询请求,并将它们批量处理。这意味着如果多个组件请求相同的数据,React Query 只会发送一次网络请求,并且将数据分发给所有请求的组件。

  4. 简洁高效和提高内存性能:通过减少不必要的网络请求和优化数据处理,React Query 可以帮助节省带宽并提高应用的响应性能。

  5. 数据同步:在复杂的应用中,保持组件间数据的同步是一个挑战。React Query 通过其高层机制,帮助保持不同组件间数据的一致性。

相关推荐
辻戋2 小时前
从零实现React Scheduler调度器
前端·react.js·前端框架
徐同保2 小时前
使用yarn@4.6.0装包,项目是react+vite搭建的,项目无法启动,报错:
前端·react.js·前端框架
Qrun3 小时前
Windows11安装nvm管理node多版本
前端·vscode·react.js·ajax·npm·html5
中国lanwp3 小时前
全局 npm config 与多环境配置
前端·npm·node.js
JELEE.4 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl6 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫7 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友7 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理9 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻9 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js