前言
在项目中,通常都需要跟服务端进行异步的数据交互,基本都是用到axios这个库来做请求,嗯,毕竟拥有80k star
,明星项目
接下来,我们来回顾下axios在项目中的使用
以查询用户信息为例,我们会这样封装
async function requestUsers(){
const {data} =await axios.get('/api/users');
return data;
}
我们再用hooks再封装下这个请求,包括loading等中间态的封装,处理的优雅一点
import React, {useState,useEffect} from 'react';
import axios from 'axios';
function useUsersQuery(){
const [data,setData] = useState([]);
const [isLoading,setLoading] = useState(false);
const [isError,setError] = useState(false)
useEffect(()=>{
(async()=>{
setLoading(true);
try{
const {data} = await axios.get('/api/users');
setData(data);
} catch((()=>{
setError(true);
})
setLoading(false);
})()
})
return {
data,
isLoading,
isError
};
}
function UserList(){
const {data, isLoading,isError} = useUsersQuery();
if (isLoading) {
return <div>loading</div>;
}
if (isError) {
return <div>error</div>;
}
return (
<div>
{
data.map((item)=>{
return <div>{item.name}</div>
})
}
</div>
)
}
可以看到,我们的项目中基本上是这样封装请求,我们不仅要请求数据,还要处理相应的loading,error这些中间态,这类通用的中间状态处理逻辑可能在不同组件中重复写很多次。
另外,现在的前端项目特别是单页面应用,会使用Flux、Redux、Mobox等状态管理库,会把组件间共享的数据都存放在状态管理库中,这些可以分为两类,一类是用户交互的中间状态,比如isLoading,isClose,modalVisible
等等,另外一类就是服务端状态(数据)
我们一般处理的方式都是无差别的存放在全局状态管理上,状态管理库为了兼容异步请求,就有了redux-saga,redux-action
这些异步解决方案
其实对于redux
等状态管理库,本身是没有异步这个概念,只有mutation这种操作,为了支持异步,硬是强加了异步action这种操作,实际这些异步中间件就是在最后的请求回调透传了dispatch
,诸如这些情况,我们不仅将数据一锅炖放在全局状态管理上,写法上也使得项目越来越臃肿了(以至于出现后面rematch、dva方案进行简化),我们有没有想过,服务端的状态就不应该放在全局状态管理上,全局状态管理应该专门处理用户交互的中间状态
接下来,就是引出今天的主角 React Query
React Query
解决了什么问题
服务端状态有以下特点:
-
存储在远端,本地无法直接控制
-
需要异步 API 来查询和更新
-
可能在不知情的情况下,被另一个请求方更改了数据,导致数据不同步
现有的状态管理库(如 Mobx、Redux等)适用于管理客户端状态,但它们并不关心客户端是如何异步请求远端数据的,所以他们并不适合处理异步的、来自服务端的状态。
而 React Query 就是为了解决服务端状态带来的上述问题而出现的,除此之外它还带来了以下特性:
-
更方便地控制缓存
-
把对于相同数据的多个请求简化成一个
-
在后台更新过期数据
-
知道数据什么时候会「过期」
-
对于数据的变化尽可能快得做出响应
-
分页查询和懒加载等请求性能优化
-
管理服务器状态的内存和垃圾回收
-
通过结构共享(structural sharing)来缓存查询结果
请求中间态处理
function Todos() {
const { isLoading, isError, data, error } = useQuery('todos', fetchTodoList)
if (isLoading) {
return <span>Loading...</span>
}
if (isError) {
return <span>Error: {error.message}</span>
}
// also status === 'success', but "else" logic works, too
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}
React query
会自动把这些isLoading,isError请求中间态处理好,我们不必写重复逻辑,另外提多一点,对于loading场景的处理,Suspense
也支持的不错,特别是局部Loading,简直Nice!
ReactQuery 的状态管理
官网对于React Query的简述,注意global state
,你会不解,为什么React Query明明是一个请求库,跟数据状态管理又有什么关系,甚至可以处做全局状态管理
那是因为ReactQuery 会在全局维护一个服务端状态树,根据 Query key 去查找状态树中是否有可用的数据,如果有则直接返回,否则则会发起请求,并将请求结果以 Query key 为主键存储到状态树中。
ReactQuery 就将我们所有的服务端状态维护在全局,并配合它的缓存策略来执行数据的存储和更新。借助于这样的特性,我们就可以将所有跟服务端进行交互的数据从类似于 Redux
这样的状态管理工具中剥离,而全部交给 ReactQuery 来管理。
举个例子:
import React from "react";
import { useQuery, queryCache } from "react-query";
import "./styles.css";
export default function App() {
return (
<div className="App">
<h1>Shared state using react-query</h1>
<Comp1 />
<Comp2 />
</div>
);
}
function useSharedState(key, initialValue) {
const { data: state } = useQuery(key, () => queryCache.getQueryData(key), {
initialData: initialValue
});
const setState = value => queryCache.setQueryData(key, value);
return [state, setState];
}
function Comp1() {
const [count, setCount] = useSharedState("count", 1);
console.log("comp1 rendered");
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
</div>
);
}
function Comp2() {
const [count, setCount] = useSharedState("count", 2);
console.log("comp2 rendered");
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
</div>
);
}
上述方式是可以实现React Query状态管理,但是有性能问题,其实本质还是利用Context透传
,我们知道Context处理prop drilling
问题,但是有性能问题,详情可查看这篇文章 精读《React — 5 Things That Might Surprise You》
不过令人费解的是官方强调ReactQuery 的状态管理
,但是在官网例子并没有给出类似的例子,上述例子还是在官方的github仓库翻到
作者说会在一个讲座分析,后面我再深入研究,先留个坑
参考文献
-
react-query.tanstack.com/quick-start
-
github.com/tannerlinsl…
-
github.com/tannerlinsl…
-
tkdodo.eu/blog/react-…
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!