你根本不需要全局状态管理

前言

在 hooks 时代,除了富文本编辑器等前端为主导的应用以外,绝大多数的前端应用的客户端状态并不复杂,需要管理的是大多数服务端状态,所以你的应用可能完全不需要全局状态管理。context 加上一个好用的请求库 ( swr or react-query )完全可以解决你的状态管理问题。所以用好请求库(这里推荐使用 swr),就可以完全不用 redux 之类的全局状态管理工具了。

用法

你根本不需要全局状态管理

上面例子中,我们在 Avatar 以及 Content 中都需要用户的信息,而用户的信息一般都是通过后台接口获取。如果我们用 swr 请求

import useSWR from 'swr'
import { Skeleton, Avatar } from "antd"

// 这里的 fetcher 可以替换成自己喜欢的请求库(比如 axios),错误处理也可以在这里统一处理,下面例子中不做处理
const fetcher = (url) => fetch(url).then((res) => res.json())

function useUser(id) { 
  const { data, isLoading } = useSWR(`/api/user/userInfo?id=${id}`, fetcher) 
  return { user: data, isLoading }
}

function Avatar(){
  const { user , isLoading } = useUser()

  if (isLoading) {
   // 有木有觉得 loading 很方便
   return <Skeleton.Avatar active/>
  }

  return <Avatar src={user.avatar} />
}

function Content(){
  const { user , isLoading } = useUser()

  if (isLoading) {
   return <Skeleton active/>
  }

  // 直接用就完事了,自动帮你去重
  return <div>welcome back, {user.name}</div>
}

这样,所有需要用户信息数据的组件我们直接调用 useUser 这个 hooks 就可以了,所有组件都是相互 独立 的。我们不需要在父组件中传递数据,并且 swr 还会自动请求 去重缓存,相同的 key 只会有一个请求。

如果资源是不可变的,即使我们再怎么重新请求也永远不会发生任何改变,那么我们可以禁用它的所有的自动重新请求。

import useSWRImmutable from 'swr/immutable'
 
// ...
useSWRImmutable(key, fetcher, options)

它具有与普通 useSWR hook 相同的 API 接口。你还可以通过禁用以下重新请求​​选项来执行相同的操作

useSWR(key, fetcher, {
  revalidateIfStale: false,
  revalidateOnFocus: false,
  revalidateOnReconnect: false
})
 
// 相当于
useSWRImmutable(key, fetcher)

实践

post 请求如何使用 useSWR

有些表格筛选条件比较多,参数比较复杂。这时候后台给你的接口是 post 接口,而你又想在参数改变时自动请求,这时候我们这样写

const fetcher = ([url, params]) => fetch(url, {
  method: 'POST',
  body: JSON.stringify(params),
  headers: {
    'Content-Type': 'application/json'
  }
}).then((res) => res.json())

function useList(params) { 
  const { data, isLoading } = useSWR(['/api/list/listInfo', params], fetcher) 
  return { List: data, isLoading }
}

从 SWR 1.1.0 开始,object 类型的 keys 可以在内部自动被序列化。

在旧版本(< 1.1.0)中,SWR 会浅比较每次渲染时的参数,如果其中任何一个发生了变化,就会触发重新验证。

所以我们可以把所有影响表格数据的参数都放在一个对象里,这样就可以直接改变对象的值来触发重新请求了。

function List(){
  const [params, setParams] = useState({page: 1, pageSize: 10, filter:...})

  const { List , isLoading } = useList(params)

  const handleSearch = (values) => {
    setParams(values)
  }

  return <div>
    <SearchForm onSearch={handleSearch} />
    <Table dataSource={List} loading={isLoading}/>
  </div>
}

如何首次加载不请求数据

有时候我们需要首次加载不请求数据,只有用户点击按钮或者某个条件满足时才请求数据。由于 hooks 的限制,我们不能直接使用判断语句来控制请求。有两种解决方案

  1. 设置 keynullswr 就不会请求数据了
function useList(params) { 
  const { data, isLoading } = useSWR(params ? ['/api/list/listInfo', params] : null, fetcher) 
  return { List: data, isLoading }
}
  1. 使用 useSWRMutation,这个 hooks 只能手动触发,而不像 useSWR 那样会自动触发。
import useSWRMutation from 'swr/mutation'
// 实现 fetcher
// 额外的参数可以通过第二个参数 `arg` 传入
// 在下例中,`arg` 为 `'my_token'`
async function updateUser(url, { arg }: { arg: string }) {
  await fetch(url, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${arg}`
    }
  })
}
 
function Profile() {
  // 一个类似 useSWR + mutate 的 API,但是它不会自动发送请求
  const { trigger } = useSWRMutation('/api/user', updateUser, options?)
 
  return <button onClick={() => {
    // 以特定参数触发 `updateUser` 函数
    trigger('my_token')
  }}>Update User</button>
}

注意这个 hook 不会与其他 useSWRMutation hook 共享状态。

结尾

swr 还有很多其他的好处,比如 自动重试自动重连等等,还有用于订阅实时数据 ( webSocket ) 的 useSWRSubscription,用于无限加载的 useSWRInfinite 。这里就不一一介绍了。如果你想了解更多,可以看看 swr 官方文档

原文链接:https://juejin.cn/post/7223670680826888249 作者:xinglee

(0)
上一篇 2023年4月20日 上午10:26
下一篇 2023年4月20日 上午10:37

相关推荐

发表回复

登录后才能评论