用React-query解决你一半的状态管理问题

开发 前端
React-Query是一个基于hooks的数据请求库。React-Query中的Query指一个异步请求的数据源。通过使用React-Query(或SWR)这样的数据请求库,可以将服务端状态从全局状态中解放出来。

[[382128]]

 按照来源,前端有两类「状态」需要管理:

  • 用户交互的中间状态
  • 服务端状态

在陈年的老项目中,通常用Redux、Mobx这样的「全局状态管理方案」无差别对待他们。

事实上,他们有很大区别:

用户交互的中间状态

比如组件的isLoading、isOpen,这类「状态」的特点是:

  • 以「同步」的形式更新
  • 状态」完全由前端控制
  • 状态」比较独立(不同的组件拥有各自的isLoading)

这类「状态」通常保存在组件内部。

当「状态」需要跨组件层级传递,通常使用Context API。

再大范围的「状态」会使用Redux这样的「全局状态管理方案」。

服务端状态

当我们从服务端请求数据:

  1. function App() { 
  2.   const [data, updateData] = useState(null); 
  3.    
  4.   useEffect(async () => { 
  5.     const data = await axios.get('/api/user'); 
  6.     updateData(data); 
  7.   }, []) 
  8.  
  9.   // 处理data 

返回的数据通常作为「状态」保存在组件内部(如App组件的data状态)。

如果是需要复用的通用「状态」,通常将其保存在Redux这样的「全局状态管理方案」中。

这样做有2个坏处:

1.需要重复处理请求中间状态

为了让App组件健壮,我们还需要处理请求中、出错等中间状态:

  1. function App() { 
  2.   const [data, updateData] = useState(null); 
  3.   const [isError, setError] = useState(false); 
  4.   const [isLoading, setLoading] = useState(false); 
  5.    
  6.   useEffect(async () => { 
  7.     setError(false); 
  8.     setLoading(true); 
  9.     try { 
  10.       const data = await axios.get('/api/user'); 
  11.       updateData(data); 
  12.     } catch(e) { 
  13.       setError(true); 
  14.     } 
  15.     setLoading(false); 
  16.   }, []) 
  17.  
  18.   // 处理data 

这类通用的中间状态处理逻辑可能在不同组件中重复写很多次。

2.「缓存」的性质不同于「状态

不同于交互的中间状态,服务端状态更应被归类为「缓存」,他有如下性质:

  • 通常以「异步」的形式请求、更新
  • 状态」由请求的数据源控制,不由前端控制
  • 状态」可以由不同组件共享

作为可以由不同组件共享的「缓存」,还需要考虑更多问题,比如:

  • 缓存失效
  • 缓存更新

Redux一把梭固然方便。但是,区别对待不同类型「状态」能让项目更可控。

这里,推荐使用React-Query管理服务端状态。

[[382129]]

另一个可选方案是SWR[1]。你可以从这里[2]看到他们的区别

初识React-Query

React-Query是一个基于hooks的数据请求库。

我们可以将刚才的例子用React-Query改写:

  1. import { useQuery } from 'react-query' 
  2.  
  3. function App() { 
  4.   const {data, isLoading, isError} = useQuery('userData', () => axios.get('/api/user')); 
  5.    
  6.   if (isLoading) { 
  7.     return <div>loading</div>; 
  8.   } 
  9.    
  10.   return ( 
  11.     <ul> 
  12.       {data.map(user => <li key={user.id}>{user.name}</li>)} 
  13.     </ul> 
  14.   ) 

 React-Query中的Query指一个异步请求的数据源。

例子中userData字符串就是这个query独一无二的key。

可以看到,React-Query封装了完整的请求中间状态(isLoading、isError...)。

不仅如此,React-Query还为我们做了如下工作:

  • 多个组件请求同一个query时只发出一个请求
  • 缓存数据失效/更新策略(判断缓存合适失效,失效后自动请求数据)
  • 对失效数据垃圾清理

数据的CRUD由2个hook处理:

  • useQuery处理数据的查
  • useMutation处理数据的增/删/改

在下面的例子中,点击「创建用户」按钮会发起创建用户的post请求:

  1. import { useQuery, queryCache } from 'react-query'
  2.  
  3. unction App() { 
  4.  const {data, isLoading, isError} = useQuery('userData', () => axios.get('/api/user')); 
  5.  // 新增用户 
  6.  const {mutate} = useMutation(data => axios.post('/api/user', data)); 
  7.  
  8.  return ( 
  9.    <ul> 
  10.      {data.map(user => <li key={user.id}>{user.name}</li>)} 
  11.      <button 
  12.        onClick={() => { 
  13.          mutate({name'kasong', age: 99}) 
  14.        }} 
  15.      > 
  16.        创建用户 
  17.      </button> 
  18.    </ul> 
  19.  ) 

 但是点击后userData query对应数据不会更新,因为他还未失效。

所以我们需要告诉React-Query,userData query对应的缓存已经失效,需要更新:

  1. import { useQuery, queryCache } from 'react-query'
  2.  
  3. function App() { 
  4.   // ... 
  5.   const {mutate} = useMutation(userData => axios.post('/api/user', userData), { 
  6.     onSuccess: () => { 
  7.       queryCache.invalidateQueries('userData'
  8.     }   
  9.   }) 
  10.    
  11.   // ... 

通过调用mutate方法,会触发请求。

当请求成功后,会触发onSuccess回调,回调中调用queryCache.invalidateQueries,将userData对应的query缓存置为invalidate。

这样,React-Query就会重新请求userData对应query的数据。

总结

通过使用React-Query(或SWR)这样的数据请求库,可以将服务端状态从全局状态中解放出来。

这为我们带来很多好处:

  • 使用通用的hook处理请求中间状态
  • 多余请求合并
  • 针对缓存的更新/失效策略
  • Redux等「全局状态管理方案」可以更专注于「前端中间状态」处理

参考资料

[1]SWR: https://swr.vercel.app/

[2]这里: https://react-query.tanstack.com/comparison

 【编辑推荐】

 

责任编辑:姜华 来源: 魔术师卡颂
相关推荐

2023-05-26 06:30:56

2015-07-27 10:24:01

苹果中国

2020-12-04 10:11:26

Unsafejava并发包

2013-02-25 10:11:35

4GLTE商用网络

2013-11-27 15:48:56

移动中间件厂商

2018-06-03 08:49:21

2015-11-24 10:04:59

大数据公司排行

2024-04-17 07:59:26

React状态管理属性钻取

2020-10-13 17:30:45

Python代码内存

2014-11-18 10:02:28

2017-02-27 16:54:20

HTTPS网络流量互联网

2015-06-16 10:50:44

2017-02-24 13:53:38

HTTPS流量互联网

2021-12-09 08:54:20

数组数字排序

2016-12-16 13:07:30

云存储运营混合云

2010-09-17 16:21:33

系统升级

2020-07-13 09:48:58

云计算云安全数据

2022-08-27 14:45:52

人工智能自动化IT

2019-10-10 10:54:16

戴尔

2021-07-26 07:32:48

模式适配器包装器
点赞
收藏

51CTO技术栈公众号