useRequest 是一个超级强大,且生产完备的网络请求 Hooks,目前已经成为蚂蚁中台最佳实践内置网络请求方案。在蚂蚁内部中台应用,写网络请求,都推荐用 useRequest。

useRequest 可能是目前社区中最强大,最接地气的请求类 Hooks 了。可以覆盖 99% 的网络请求场景,无论是读还是写,无论是普通请求还是分页请求,无论是缓存还是防抖节流,通通都能支持。只有你想不到,没有它做不到(吹牛🐂~)。

能力介绍

基础网络请求

import { useRequest } from '@umijs/hooks';

function getUsername() {
  return Promise.resolve('jack');
}

export default () => {
  const { data, error, loading } = useRequest(getUsername)

  if (error) return <div>failed to load</div>
  if (loading) return <div>loading...</div>
  return <div>Username: {data}</div>
}

这是一个最简单的网络请求示例。在这个例子中 useRequest 接收了一个 Promise 函数。在组件初始化时,会自动触发 getUsername 执行,并自动管理 dataloadingerror 等数据,我们只需要根据状态来写相应的 UI 实现即可。

手动请求

对于“写”请求,我们一般需要手动触发,比如添加用户,编辑信息,删除用户等等。 useRequest 只需要配置 manual = true ,即可阻止初始化执行。只有触发 run 时才会开始执行。

import { useRequest } from '@umijs/hooks';

export default () => {
  const { run, loading } = useRequest(changeUsername, {manual: true})

  return (
    <Button onClick={() => run('new name')} loading={loading}>
       Edit
    </Button>
    )
}

轮询

对于需要保持新鲜度的数据,我们通常需要不断发起网络请求以更新数据。 useRequest 只要配置 poilingInterval 即可自动定时发起网络请求。

import { useRequest } from '@umijs/hooks';

export default () => {
  const { data } = useRequest(getUsername, { pollingInterval: 1000 })

  return <div>Username: {data}</div>
}

同时通过设置 pollingWhenHidden ,我们可以智能的实现在屏幕隐藏时,暂停轮询。等屏幕恢复可见时,继续请求,以节省资源。

当然你也可以通过 run/cancel 来手动控制定时器的开启和关闭

并行请求

什么是并行请求?看了下图应该就明白了,也就是同一个接口,我们需要维护多个请求状态。

示例中的并行请求有几个特点:

  • 删除 n 个不同的用户,则需要维护 n 个请求状态。
  • 多次删除同一个用户,则只需要维护最后一个请求。

useRequest 通过设置 fetchKey ,即可对请求进行分类。相同分类的请求,只会维护一份状态。不同分类的请求,则会维护多份状态。在下面的代码中,我们通过 userId 将请求进行分类,同时我们可以通过 fetches[userId] 拿到当前分类的请求状态!

export default () => {
  const { run, fetches } = useRequest(deleteUser, {
    manual: true,
    fetchKey: id => id, // 不同的 ID,分类不同
  });

  return (
    <div>
      <Button loading={fetches.A?.loading} onClick={() => { run('A') }}>删除 1</Button>
      <Button loading={fetches.B?.loading} onClick={() => { run('B') }}>删除 2</Button>
      <Button loading={fetches.C?.loading} onClick={() => { run('C') }}>删除 3</Button>
    </div>
  );
};

防抖 & 节流

通常在边输入边搜索的场景中,我们会用到防抖功能,以节省不必要的网络请求。通过 useRequest ,只需要配置一个 debounceInterval ,就可以非常简单的实现对网络请求的节流操作。

import { useRequest } from '@umijs/hooks';

export default () => {
  const { data, loading, run, cancel } = useRequest(getEmail, {
    debounceInterval: 500,
    manual: true
  });

  return (
    <div>
      <Select onSearch={run} loading={loading}>
        {data && data.map(i => <Option key={i} value={i}>{i}</Option>)}
      </Select>
    </div>
  );
};

节流与防抖是同样的道理,只需要配置了 throttleInterval ,即可实现

缓存 & SWR & 预加载

在前面我讲了什么是 SWR,在 SWR 场景下,我们会对接口数据进行缓存,当下次请求该接口时,我们会先返回缓存的数据,同时,在背后发起新的网络请求,待新数据拿到后,重新触发渲染。

对于一些数据不是经常变化的接口,使用 SWR 后,可以极大提高用户使用体验。比如下面的图片例子,当我们第二次访问该文章时,直接返回了缓存的数据,没有任何的等待时间。同时,我们可以看到“最新访问时间”在 2 秒后更新了,这意味着新的请求数据返回了。

useRequest 通过配置 cacheKey ,即可进入 SWR 模式,相当简单。

  const { data, loading } = useRequest(getArticle, {
    cacheKey: 'articleKey',
  });

同时需要注意,同一个 cacheyKey 的数据是全局共享的。通过这个特性,我们可以实现“预加载”功能。比如鼠标 hover 到文章标题时,我们即发送读取文章详情的请求,这样等用户真正点进文章时,数据早已经缓存好了