来源
按照来源,前端有两类「状态」需要管理:
- 用户交互的中间状态
简单的用 useStatue useContext 进行管理,复杂的用 react redux
- 服务端状态
可以自己封装 hooks 进行处理,但是涉及多余请求合,并缓存,重连,重试等状态就比较复杂了
Tanner Linsley 开发的好用的处理服务器数据状态的 hooks
react-query 是专门做服务端状态处理的不但可以应对 api 数据也可以应对 graphql
中文文档
英文文档
数据的CRUD由2个hook处理:
- useQuery 处理数据的查
- useMutation 处理数据的增/删/改
缓存
SWR(stale-while-revalidate)
缓存表示的是当将当前请求成功的数据缓存起来,在组件重新加载的时候,如果有缓存数据,会优先返回缓存数据,然后在背后发送新请求
缓存失效的时间是当一个 query key 的数据没有挂载点或没有观察者(界面上没有引用)的时候,开始进行计时, 时间到这个 query key
就会在缓存内删除,如果在这段时间内 query key 有了新的挂载点,缓存就不会失效
保鲜的失效的开始计算时间是跟随缓存失效倒计时的开始时间的,同时保鲜的时间段是小于等于缓存的时间段的
有俩个参数可以设置
cacheTime 缓存时间
如果数据在缓存时间内,判断 staleTime 是否大于0 ,如果大于 0 进行 staleTime 判断,否则返回缓存数据,在进行数据查询,
之后用查询回来的数据更新界面及缓存 ,如果数据不在缓存时间内,立即进行查询
此数值默认为5分钟
staleTime 数据保鲜时间
如果数据还在缓存时间里,则判断数据是否在保鲜时间内,如果在的话,就不会发起查询,直接用缓存数据,不在的话先返回缓存数据,在进行数据查询,
之后用查询回来的数据更新界面及缓存
此数值默认为0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| function getName(): Promise<string> { return new Promise((resolve, reject) => { var rand = Boolean(Math.round(Math.random())); const time = Math.random() > 0.5 ? 3000 : 500;
if (rand === false) { setTimeout(() => { console.log("发生错误"); reject(new Error("发生了错误")); }, time); } else { setTimeout(() => { console.log("获取数据成功"); resolve(Mock.mock("@name")); }, time); } });
function FuncComp({ name}: { name: string}) { console.log(name + ":rending");
const { data, error, isLoading, isError, isFetching, refetch } = useQuery(["key1"], () => getName(), { refetchOnWindowFocus: false, staleTime: 3 * 1000, cacheTime: 15 * 1000, });
return ( <div> {isLoading && <> {name}:Loading...</>} <br/> {isFetching && <>{name}:Fetching...</>} <br/> {isError && (<>{name}:{(error as Error).message} </>)} <br/> {data && (<>{name}:{data}</>)} <br/> <br/>
<button // 强制刷新数据 onClick={() => refetch()}> {name}+:refetch </button> </div> ); } }
function App() { const [status, { toggle }] = useToggle() return ( <> {status && <FuncComp name="组件A" para={true} ></FuncComp>} <br/> <button onClick={toggle}>显示组件 FuncComp</button> </> ); }
|
只要有挂载点或观察者缓存永远不会失效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function App() { const [status, { toggle }] = useToggle() return ( <> {status ? <FuncComp name="组件A" para={true} ></FuncComp>:<FuncComp name="组件B" para={true} ></FuncComp>} <br/> <button onClick={toggle}>显示组件 FuncComp</button> </> ); }
|
query key 内包含状态数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| function FuncComp({ name}: { name: string}) { console.log(name + ":rending");
const [status, setStatus] = useState(false); const { data, error, isLoading, isError, isFetching, refetch } = useQuery(["key1", status], () => getName(), { refetchOnWindowFocus: false, staleTime: 15 * 1000, cacheTime: 15 * 1000, });
return ( <div> {isLoading && <> {name}:Loading...</>} <br/> {isFetching && <>{name}:Fetching...</>} <br/> {isError && (<>{name}:{(error as Error).message} </>)} <br/> {data && (<>{name}:{data}</>)} <br/> <br/> <button // 可以通过改变 queryKey 重新获取数据 onClick={() => { setStatus((x) => !x); }} > {name}:click_{String(status)} </button>
<button onClick={() => refetch()}> {name}+:refetch </button> </div> ); }
|