axios-hooks vs react-query vs swr vs use-http
React 数据请求库架构对比:从 HTTP 客户端到服务端状态管理
axios-hooksreact-queryswruse-http类似的npm包:

React 数据请求库架构对比:从 HTTP 客户端到服务端状态管理

axios-hooks、react-query、swr 和 use-http 都是 React 生态中用于处理数据请求的工具,但它们的定位截然不同。react-query(现已迁移至 @tanstack/react-query)和 swr 属于服务端状态管理库,专注于缓存、同步、后台更新和请求去重,适合复杂的数据驱动应用。axios-hooks 是 axios 的 React Hook 封装,保留了 axios 的全部特性(如拦截器),适合深度依赖 axios 生态的项目。use-http 则提供更细粒度的方法钩子(如 useGet、usePost), API 简单但功能相对基础。选择哪款库取决于你是需要强大的缓存策略,还是仅仅需要一个更方便的 HTTP 客户端封装。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
axios-hooks01,89247.1 kB61 年前MIT
react-query050,1892.26 MB1244 年前MIT
swr032,466320 kB21411 天前MIT
use-http02,300224 kB923 年前MIT

React 数据请求库架构对比:从 HTTP 客户端到服务端状态管理

在 React 开发中,处理数据请求不仅仅是发送一个 HTTP 报文。我们需要考虑缓存、 loading 状态、错误处理、请求竞态以及数据同步。axios-hooks、react-query、swr 和 use-http 代表了四种不同的解决思路。本文将深入对比它们的核心机制,帮助你在架构选型时做出明智决定。

🏗️ 核心定位:状态管理 vs 客户端封装

这是选型时最重要的分水岭。

react-query 和 swr 是服务端状态管理库。它们不仅仅发送请求,还会缓存响应数据,管理数据的生命周期(如 stale 时间),并在窗口聚焦时自动重新验证数据。它们认为服务器数据是“唯一事实来源”,本地只是缓存。

axios-hooks 和 use-http 本质上是HTTP 客户端的 Hook 封装。它们主要解决的是在组件中发送请求的便利性问题,默认行为更接近传统的请求 - 响应模式,缓存能力较弱或需要手动配置。

// react-query/swr: 自动缓存 key 对应的数据
useQuery(['user', id], fetchUser); 
// 下次组件挂载,若缓存未过期,直接返回数据,不发请求

// axios-hooks/use-http: 默认每次渲染或依赖变化都可能触发请求
useAxios({ url: `/user/${id}` }); 
// 除非手动配置缓存,否则行为更像标准 useEffect 请求

🔄 缓存与重新验证策略

对于现代 Web 应用,缓存策略直接影响用户体验和服务器负载。

swr 主打 Stale-While-Revalidate 策略。它会先返回缓存中的旧数据(Stale),同时在后台发送请求验证数据(Revalidate),有新数据后更新界面。配置非常简洁。

// swr: 默认开启聚焦重新验证
import useSWR from 'swr';

function Profile() {
  const { data, error } = useSWR('/api/user', fetcher);
  // 窗口重新聚焦时自动重发请求
}

react-query 提供更细粒度的控制。你可以设置 staleTime(数据多久变旧)、cacheTime(缓存多久被删除)以及 refetchOnWindowFocus。

// react-query: 精细控制缓存生命周期
import { useQuery } from '@tanstack/react-query';

function Profile() {
  const { data } = useQuery({
    queryKey: ['user'],
    queryFn: fetchUser,
    staleTime: 5 * 60 * 1000, // 5 分钟内数据视为新鲜
    cacheTime: 10 * 60 * 1000  // 缓存保留 10 分钟
  });
}

axios-hooks 默认不管理缓存。它依赖 axios 实例。如果你需要缓存,通常需要配合 axios-cache-adapter 或手动实现,这增加了架构复杂度。

// axios-hooks: 默认无缓存,每次渲染逻辑触发请求
import { useAxios } from 'axios-hooks';

function Profile() {
  const { data, loading } = useAxios('/api/user');
  // 每次组件挂载都会发请求,除非手动管理缓存逻辑
}

use-http 提供基础的 loading/error 状态管理,但缓存能力有限。它更侧重于按 HTTP 方法组织代码。

// use-http: 按方法拆分钩子
import { useGet } from 'use-http';

function Profile() {
  const { get, data, loading } = useGet('/api/user');
  // 适合简单场景,复杂缓存需自行实现
}

✍️ 数据突变(Mutations)处理

处理 POST/PUT/DELETE 请求时,如何更新本地缓存是关键差异点。

react-query 拥有最强大的 useMutation。它可以自动使查询失效(invalidateQueries),或者乐观更新(optimisticUpdate)UI,无需刷新页面。

// react-query: 突变后自动刷新列表
const queryClient = useQueryClient();
const mutation = useMutation({
  mutationFn: updateUser,
  onSuccess: () => {
    queryClient.invalidateQueries(['users']); // 自动触发列表重新加载
  }
});

swr 提供 mutate 函数,可以手动更新缓存或触发重新验证。灵活但需要手动调用。

// swr: 手动更新缓存
import useSWR, { mutate } from 'swr';

const onSave = async () => {
  await api.updateUser();
  mutate('/api/user'); // 手动触发重新验证
};

axios-hooks 和 use-http 没有内置的突变缓存联动机制。你需要在请求成功后手动刷新组件或调用全局状态更新。

// axios-hooks: 手动触发刷新
const { refetch } = useAxios('/api/user');

const onSave = async () => {
  await api.postUser();
  refetch(); // 手动重新拉取
};

🛡️ 错误处理与加载状态

所有库都提供了 loading 和 error 状态,但细节体验不同。

react-query 和 swr 将 isLoading、isError、isValidating 等状态暴露得非常清晰,且支持重试机制配置。

// react-query: 丰富的状态标识
const { isLoading, isError, error, data } = useQuery({ ... });
if (isLoading) return <Spinner />;
if (isError) return <Error msg={error.message} />;

axios-hooks 返回 loading、error、response 三元组,符合常见习惯。

// axios-hooks: 标准三元组
const { loading, error, response } = useAxios(url);

use-http 类似,但不同方法钩子(get/post)返回结构一致,便于统一处理。

// use-http: 统一结构
const { loading, error, data } = useGet(url);

🧩 实际场景代码对比

假设我们需要获取用户列表,支持搜索过滤,并在添加用户后刷新列表。

方案 A: react-query (推荐复杂场景)

// 查询
const { data } = useQuery({
  queryKey: ['users', filter],
  queryFn: () => fetch(`/api/users?q=${filter}`)
});

// 突变
const addMutation = useMutation({
  mutationFn: addUser,
  onSuccess: () => queryClient.invalidateQueries(['users'])
});

方案 B: swr (推荐轻量场景)

// 查询
const { data } = useSWR(`/api/users?q=${filter}`, fetcher);

// 突变
const onSave = async () => {
  await addUser();
  mutate(`/api/users?q=${filter}`);
};

方案 C: axios-hooks (推荐 Axios 深度用户)

// 查询
const { data, loading } = useAxios(`/api/users?q=${filter}`);

// 突变
const { run } = useAxios({ url: '/api/users', method: 'POST' }, { manual: true });
const onSave = () => run().then(() => refreshList());

方案 D: use-http (推荐简单 CRUD)

// 查询
const { data, loading } = useGet(`/api/users?q=${filter}`);

// 突变
const { post } = usePost('/api/users');
const onSave = () => post().then(() => window.location.reload());

📊 总结对比表

特性react-queryswraxios-hooksuse-http
核心定位服务端状态管理轻量状态管理HTTP 客户端封装HTTP 方法封装
缓存策略⭐⭐⭐⭐⭐ (高度可配)⭐⭐⭐⭐ (默认优秀)⭐ (需额外配置)⭐⭐ (基础)
突变管理⭐⭐⭐⭐⭐ (自动失效)⭐⭐⭐ (手动 mutate)⭐⭐ (手动 refetch)⭐⭐ (手动)
axios 集成需配置适配器需配置适配器⭐⭐⭐⭐⭐ (原生支持)⭐⭐⭐ (支持)
包体积较大小中小
维护状态⭐⭐⭐⭐⭐ (TanStack)⭐⭐⭐⭐⭐ (Vercel)⭐⭐⭐ (稳定)⭐⭐ (较慢)

💡 架构师建议

  1. 关于 react-query 的命名:请注意,react-query v4 及以上版本已迁移至 @tanstack/react-query。在新项目中,请直接使用 @tanstack/react-query,旧包名仅用于维护旧项目。
  2. 不要混用:尽量避免在一个项目中同时使用 swr 和 react-query。它们的缓存层是独立的,混用会导致数据不一致和内存浪费。
  3. axios 用户的选择:如果你必须使用 axios 的高级特性(如请求拦截器处理 Token),axios-hooks 是最平滑的过渡方案。但如果你需要强大的缓存,建议配合 react-query 的 queryFn 使用 axios 实例,而不是直接用 axios-hooks。
  4. 长期维护风险:use-http 的更新频率相对较低。在核心业务系统中,优先选择背后有大公司背书(如 Vercel 的 swr 或 TanStack 的 query)的库,以降低长期维护风险。

🎯 最终结论

  • 大型复杂应用:首选 @tanstack/react-query。它的功能深度能支撑复杂的业务逻辑。
  • Next.js 或轻量应用:首选 swr。它与 Next.js 集成最好,配置最少。
  • 遗留项目或 Axios 重度依赖:选择 axios-hooks。它能最大化复用现有基础设施。
  • 简单后台或原型:可以考虑 use-http,但需做好未来迁移的准备。

选择工具不仅是选择 API,更是选择一种数据流管理哲学。希望这篇对比能帮助你构建更稳健的前端架构。

如何选择: axios-hooks vs react-query vs swr vs use-http

  • axios-hooks:

    如果你的项目已经深度依赖 axios 实例(例如配置了全局拦截器、认证头),且不需要复杂的服务端状态缓存,axios-hooks 是最佳选择。它提供了 useAxios 钩子,能够无缝复用现有的 axios 配置,支持手动触发请求和自动取消。适合中小型项目或只需要简单封装 HTTP 客户端的场景。

  • react-query:

    选择 react-query(建议直接使用新版 @tanstack/react-query)如果你需要企业级的服务端状态管理能力。它提供最丰富的功能,包括强大的缓存控制、乐观更新、无限加载、突变(Mutation)管理和优秀的 DevTools。适合大型复杂应用,尤其是需要处理大量同步、缓存失效和后台刷新逻辑的场景。

  • swr:

    选择 swr 如果你偏好轻量级、约定优于配置的方案,特别是身处 Vercel/Next.js 生态中。它内置了“ stale-while-revalidate ”策略,默认配置即可满足大多数场景,API 极其简洁。适合追求快速开发、对缓存策略要求不是极度复杂的博客、内容展示或中小型 Dashboard 应用。

  • use-http:

    选择 use-http 如果你希望按 HTTP 方法拆分钩子(如 useGet、usePost),且项目规模较小,不需要复杂的缓存同步机制。它的 API 直观,上手成本低。但需注意其社区活跃度和维护频率低于 swr 和 react-query,在长期维护的大型项目中需评估风险。

axios-hooks的README

The license of this software has changed to AWISC - Anti War ISC license

axios-hooks

ci codecov npm version bundlephobia

React hooks for axios, with built-in support for server side rendering.

Features

  • All the axios awesomeness you are familiar with
  • Zero configuration, but configurable if needed
  • One-line usage
  • Super straightforward to use with SSR

Installation

npm install axios axios-hooks

axios is a peer dependency and needs to be installed explicitly

Version information

Quick Start

Edit axios-hooks Quick Start

import useAxios from 'axios-hooks'

function App() {
  const [{ data, loading, error }, refetch] = useAxios(
    'https://reqres.in/api/users?delay=1'
  )

  if (loading) return <p>Loading...</p>
  if (error) return <p>Error!</p>

  return (
    <div>
      <button onClick={refetch}>refetch</button>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  )
}

Documentation

API

Examples

Guides

API

The package exports one default export and named exports:

import useAxios, {
  configure,
  loadCache,
  serializeCache,
  makeUseAxios
} from 'axios-hooks'

useAxios(url|config, options)

The main React hook to execute HTTP requests.

  • url|config - The request URL or config object, the same argument accepted by axios.
  • options - An options object.
    • manual ( false ) - If true, the request is not executed immediately. Useful for non-GET requests that should not be executed when the component renders. Use the execute function returned when invoking the hook to execute the request manually.
    • useCache ( true ) - Allows caching to be enabled/disabled for the hook. It doesn't affect the execute function returned by the hook.
    • ssr ( true ) - Enables or disables SSR support
    • autoCancel ( true ) - Enables or disables automatic cancellation of pending requests whether it be from the automatic hook request or from the manual execute method

[!IMPORTANT]
Default caching behavior can interfere with test isolation. Read the testing section for more information.

Returns

[{ data, loading, error, response }, execute, manualCancel]

  • data - The success response data property (for convenient access).

  • loading - True if the request is in progress, otherwise False.

  • error - The error value

  • response - The whole success response object.

  • execute([config[, options]]) - A function to execute the request manually, bypassing the cache by default.

    • config - Same config object as axios, which is shallow-merged with the config object provided when invoking the hook. Useful to provide arguments to non-GET requests.
    • options - An options object.
      • useCache ( false ) - Allows caching to be enabled/disabled for this "execute" function.

    Returns

    A promise containing the response. If the request is unsuccessful, the promise rejects and the rejection must be handled manually.

  • manualCancel() - A function to cancel outstanding requests manually.

configure({ cache, axios, defaultOptions })

Allows to provide custom instances of cache and axios and to override the default options.

  • cache An instance of lru-cache, or false to disable the cache
  • axios An instance of axios
  • defaultOptions An object overriding the default Hook options. It will be merged with the default options.

serializeCache()

Dumps the request-response cache, to use in server side rendering scenarios.

Returns

Promise<Array> A serializable representation of the request-response cache ready to be used by loadCache

loadCache(cache)

Populates the cache with serialized data generated by serializeCache.

  • cache The serializable representation of the request-response cache generated by serializeCache

makeUseAxios({ cache, axios, defaultOptions })

Creates an instance of the useAxios hook configured with the supplied cache, axios instance and default options.

  • cache An instance of lru-cache, or false to disable the cache
  • axios An instance of axios
  • defaultOptions An object overriding the default Hook options. It will be merged with the default options.

Returns

An instance of useAxios React Hook which will always use the provided cache and axios instance.

The returned value, besides being a function that can be used as a React Hook, also contains the properties:

  • resetConfigure
  • configure
  • loadCache
  • serializeCache

which are the same as the package's named exports but limited to the useAxios instance returned by makeUseAxios.

Refresh Behavior

The arguments provided to useAxios(config[,options]) are watched for changes and compared using deep object comparison.

When they change, if the configuration allows a request to be fired (e.g. manual:false), any pending request is canceled and a new request is triggered, to avoid automatic cancellation you should use autoCancel:false option

Because of this, it's important to make sure that the arguments to useAxios preserve deep equality across component renders. This is often the case unless functions (e.g. axios transformers) are provided to a configuration object. In that case, those functions need to be memoized or they will trigger a request execution at each render, leading to an infinite loop.

Configuration

Unless provided via the configure function, axios-hooks uses as defaults:

  • axios - the default axios package export
  • cache - a new instance of the default lru-cache package export, with no arguments
  • defaultOptions - { manual: false, useCache: true, ssr: true, autoCancel: true }

These defaults may not suit your needs, for example:

  • you may want a common base url for axios requests
  • the default (Infinite) cache size may not be a sensible default
  • you want to disable caching altogether

In such cases you can use the configure function to provide your custom implementation of both.

When configure is used, it should be invoked once before any usages of the useAxios hook

Example

Edit axios-hooks configuration example

import { configure } from 'axios-hooks'
import LRU from 'lru-cache'
import Axios from 'axios'

const axios = Axios.create({
  baseURL: 'https://reqres.in/api'
})

const cache = new LRU({ max: 10 })

configure({ axios, cache })

Manual Requests

On the client, requests are executed when the component renders using a React useEffect hook.

This may be undesirable, as in the case of non-GET requests. By using the manual option you can skip the automatic execution of requests and use the return value of the hook to execute them manually, optionally providing configuration overrides to axios.

Example

In the example below we use the useAxios hook twice. Once to load the data when the component renders, and once to submit data updates via a PUT request configured via the manual option.

Edit axios-hooks Manual Request

import useAxios from 'axios-hooks'

function App() {
  const [{ data: getData, loading: getLoading, error: getError }] = useAxios(
    'https://reqres.in/api/users/1'
  )

  const [{ data: putData, loading: putLoading, error: putError }, executePut] =
    useAxios(
      {
        url: 'https://reqres.in/api/users/1',
        method: 'PUT'
      },
      { manual: true }
    )

  function updateData() {
    executePut({
      data: {
        ...getData,
        updatedAt: new Date().toISOString()
      }
    })
  }

  if (getLoading || putLoading) return <p>Loading...</p>
  if (getError || putError) return <p>Error!</p>

  return (
    <div>
      <button onClick={updateData}>update data</button>
      <pre>{JSON.stringify(putData || getData, null, 2)}</pre>
    </div>
  )
}

Manual Cancellation

The cancellation method can be used to cancel an outstanding request whether it be from the automatic hook request or from the manual execute method.

Example

In the example below we use the useAxios hook with its automatic and manual requests. We can call the cancellation programmatically or via controls.

function App() {
  const [pagination, setPagination] = useState({})
  const [{ data, loading }, refetch, cancelRequest] = useAxios({
    url: '/users?delay=5',
    params: { ...pagination }
  })

  const handleFetch = () => {
    setPagination({ per_page: 2, page: 2 })
  }

  const externalRefetch = async () => {
    try {
      await refetch()
    } catch (e) {
      // Handle cancellation
    }
  }

  return (
    <div>
      <button onClick={handleFetch}>refetch</button>
      <button onClick={externalRefetch}>External Refetch</button>
      <button disabled={!loading} onClick={cancelRequest}>
        Cancel Request
      </button>
      {loading && <p>...loading</p>}
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  )
}

Server Side Rendering

axios-hooks seamlessly supports server side rendering scenarios, by preloading data on the server and providing the data to the client, so that the client doesn't need to reload it.

How it works

  1. the React component tree is rendered on the server
  2. useAxios HTTP requests are executed on the server
  3. the server code awaits serializeCache() in order to obtain a serializable representation of the request-response cache
  4. the server injects a JSON-serialized version of the cache in a window global variable
  5. the client hydrates the cache from the global variable before rendering the application using loadCache

Example

Edit axios-hooks SSR example

<!-- fragment of the HTML template defining the window global variable -->

<script>
  window.__AXIOS_HOOKS_CACHE__ = {{{cache}}}
</script>
// server code for the server side rendering handler

import { serializeCache } from 'axios-hooks'

router.use(async (req, res) => {
  const index = fs.readFileSync(`${publicFolder}/index.html`, 'utf8')
  const html = ReactDOM.renderToString(<App />)

  // wait for axios-hooks HTTP requests to complete
  const cache = await serializeCache()

  res.send(
    index
      .replace('{{{html}}}', html)
      .replace('{{{cache}}}', JSON.stringify(cache).replace(/</g, '\\u003c'))
  )
})
// client side code for the application entry-point

import { loadCache } from 'axios-hooks'

loadCache(window.__AXIOS_HOOKS_CACHE__)

delete window.__AXIOS_HOOKS_CACHE__

ReactDOM.hydrate(<App />, document.getElementById('root'))

Multiple Hook Instances

Sometimes it is necessary to communicate with different APIs or use different caching strategies for different HTTP interactions.

makeUseAxios allows to create multiple instances of the useAxios React Hook which can be configured and managed independently.

In other words, makeUseAxios is a factory of useAxios, which returns a React Hook configured against the provided axios or cache instances.

This feature can also be used to create a single pre configured React Hook instance as an alternative to the global configure feature

Example

Edit axios-hooks makeUseAxios

import axios from 'axios'
import { makeUseAxios } from 'axios-hooks'

const useAxios = makeUseAxios({
  axios: axios.create({ baseURL: 'https://reqres.in/api' })
})

function App() {
  const [{ data, loading, error }, refetch] = useAxios('/users?delay=1')

  if (loading) return <p>Loading...</p>
  if (error) return <p>Error!</p>

  return (
    <div>
      <button onClick={refetch}>refetch</button>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  )
}

Testing

Testing components that make use of the useAxios hook are susceptible to test isolation leakage because of default caching behavior. The following snippets can be used to disable caching while testing:

react-testing-library

beforeAll(() => {
  useAxios.configure({ cache: false })
})

Promises

axios-hooks depends on a native ES6 Promise implementation to be supported. If your environment doesn't support ES6 Promises, you can polyfill.

Credits

axios-hooks is heavily inspired by graphql-hooks, developed by the awesome people at NearForm.

License

MIT