useswr基础使用和问题总结

一、useswr 返回的方法的使用技巧

useSwr 使用分析

第一个参数

1、第一个参数是key ,下文是写成 getKey() 方法 返回的数据会给到第二个参数的入参。
2、写成 key ,要保证全局是唯一的!重复的key 会被useswr认为是同一个,使用同样的缓存
3、分页接口的情况下,必须写成 getKey() 方法,getKey的依赖项变化时,会导致 接口从第一页重新请求。
4、return null 就是不调接口。

第二个参数

1、就是接口函数,普通的useswr 直接用接口函数即可
2、下文的代码中是由于分页接口中getkey依赖项参数可能一致,所以写成了这种 return [queryPackagesList, params],保证每个分页的key都不一样。因此 第二个参数接收的时候也是[, v]=> queryPackagesList({ …v, per_page })

第三个参数
是配置项。具体的见本文最后一小节。

  const { data = [], size, setSize, isValidating, mutate } = useSWRInfinite(
    (pageIndex: number, previousPageData: ListProps<ItemProps>) => {
      // 首页,没有 `previousPageData`
      if (pageIndex === 0) {
        return [queryPackagesList, params];
      }
      // 加载更多
      if (previousPageData && previousPageData.next) {
        return [queryPackagesList, { ...params, ...previousPageData.next }];
      }

      // 最后一页
      return null;
    },
    ([, v]) => queryPackagesList({ ...v, per_page }),
    useSWRInfiniteDefOption,
  );

mutate用途:

1、mutate()从某一个useSWR里return出来,是用作当前key值下强制请求。主要用于刷新、重复点击查询按钮等功能

2、更新其他接口缓存里的data , mutate(key,data,option)

getKey的依赖项:

1、getKey的依赖项params 变化的时候,即setParams(newParams)会引起useSWRInfinite 触发重新请求。

2、若 hashCode(query) === hashCode(params) ,即params属性没发生变化,则useSWRInfinite 不会重新请求,如此时有需求,需要重新加载,则用mutate()来实现。

示例代码:
 const { items: customerList, isLoadingList, has_next, size, setSize, mutate } = useCustomerList( params );

useEffect(() => { 
    const query = { ...baseParams, ...getParamsFromUrl(location.query), }; setParams(query);  
    if (hashCode(query) === hashCode(params)) mutate(); 
}, [location]);

setSize()的用途:

1、setSize()是useSWRInfinite return出来的一个方法。通过setSize(size+1)实现加载下一页的功能。

2、setSize(1) 会把当前useSWRInfinite 的size重新置为1,只展示第一页数据,如已经加载过,则用的是缓存数据,此时不调用接口。

问题1: 如何实现 getkey函数的依赖项不变的情况,重新请求?

使用场景:

在点击查询或重置按钮时,产品这边的需要是,在点击后,应重新查找并展示其相应的第一页内容

问题描述:

使用mutate+setSize方法来实现,虽页面上给到的结果是正确的,但有可能导致重复调用api的情况

原因分析:

使用useSWRInfinite分页加载数据,(getkey依赖项)相同参数下,若重复点击查询,直接使用mutate(),会导致将当前已加载过的页数,再次全部重新请求并展示。 例如,目前加载了10页数据。参数(getkey依赖项)不变的情况下,调用mutate(),会将前10页的数据再次请求并展示出来。

解决方法:

如果某种场景下,必须实现,重复点击查询按钮,只调用并展示第一页数据。给出3种解决方案。经过讨论分析,使用方法3 。

方法 1

const newParams: UseListProps = {
      ...baseParams,
      ...getParamsFromUrl(location.query),//有变动的参数
    };
    setParams(newParams);
    if (hashCode(newParams) === hashCode(params)) {setSize(1); mutate()}

该方法存在的问题:

先把页面变为1 ,再使用mutate()强制重新请求api,此时获取到的第一页的数据是数据库中给出的最新内容,后续点击加载更多,如已经调用过的相关接口(点击查询以前,对相同的参数,进行过加载),则仍使用缓存数据,如未调用过,则重新请求。

方法 2

...同上    
if (hashCode(newParams) === hashCode(params)) {mutate();setSize(1); }

该方法存在的问题:

先mutate()强制重新请求数据,此时会重新调用当前已加载过的所有接口。setSize(1)只有第一页展示出来

方法3

let newParams: UseListProps = {
      ...baseParams,
      ...getParamsFromUrl(location.query),//有变动的参数
    };    
if (hashCode(newParams) === hashCode(params))  newParams={...newParams, date:+new Date()}
setParams(newParams)

分析:在点击查询的时候给params里加个时间戳,通过更新getkey的依赖项实现此功能。强制促使getKey的依赖项发生变化,从而引起useSWRInfinite 触发重新请求,且不再使用缓存。

问题2:如何实现getKey依赖项变化,但是接口不重新请求?

点击加载更多,切换分页后,无法正确加载数据问题

问题描述:

使用useSWRInfinite分页加载数据,per_page放到getKey的依赖项params里,会导致改变per_page后,无法正确加载数据问题。
例如,目前加载了第一页,每页5条数据,切换为每页10条,再点击加载更多,会导致仍是per_page=5。若setParams(newParams)则会导致只加载前10条。

原因分析:

useSWRInfinite的第一个参数getKey会有一个依赖项params(即接口入参),一旦setParams(newParams)被调用,就触发数据重新加载。且从第一页开始加载。

export const useCustomerList = (params: UseCustomerListProps) => {
  const { data, size, setSize, mutate, isValidating } = useSWRInfinite(
    (pageIndex: number, previousPageData: ListProps<CustomerInfo>) => {
      // 首页,没有 `previousPageData`
      if (pageIndex === 0) {
        return [getCustomerInfoList, params];
      }
      // 加载更多
      if (previousPageData && previousPageData.next) {
        return [getCustomerInfoList, { ...params, ...previousPageData.next }];
      }

      // 最后一页
      return null;
    },
    ([, v]) => getCustomerInfoList(v),
    { ...useSWRInfiniteDefOption, revalidateOnMount: false },
  );
  const items = data?.flatMap((value) => value?.items || []) || [];
  const has_next = data && data[data?.length - 1]?.has_next;
  return { items, has_next, isLoadingList: isValidating, size, setSize, mutate };
};

解决办法:

此种情况下,只能解绑per_page和params(getkey的依赖项),把per_page作为第二单独参数传到自定义hook。使得per_page的变化并不会导致依赖项的变化,这样useSWRInfinite也不会触发重新验证的机制。

export const useCustomerList = (params: UseCustomerListProps, per_page: string) => {
  const { data, size, setSize, mutate, isValidating } = useSWRInfinite(
    (pageIndex: number, previousPageData: ListProps<CustomerInfo>) => {
      // 首页,没有 `previousPageData`
      if (pageIndex === 0) {
        return [getCustomerInfoList, params];
      }
      // 加载更多
      if (previousPageData && previousPageData.next) {
        return [getCustomerInfoList, { ...params, ...previousPageData.next }];
      }

      // 最后一页
      return null;
    },
    ([, v]) => getCustomerInfoList({ ...v, per_page }),
    { ...useSWRInfiniteDefOption, revalidateOnMount: false },
  );
  const items = data?.flatMap((value) => value?.items || []) || [];
  const has_next = data && data[data?.length - 1]?.has_next;
  return { items, has_next, isLoadingList: isValidating, size, setSize, mutate };
};

SWR的配置属性说明

useSWR通用配置

export const useSWRDefOption = {
  // 以下配置(useSWRInfinite正常生效)
  revalidateOnMount: true, // 默认true   是否在页面onMount时,自动请求
  shouldRetryOnError: true, // 默认true  在出现错误时 SWR 使用指数退避算法重发请求。该算法允许应用从错误中快速恢复,而不会浪费资源频繁地重试。你还可以通过 onErrorRetry 选项覆盖该行为 

  // 以下配置 (useSWRInfinite 需要配合revalidateFirstPage: true,且重新加载时,只会加载第一页)
  refreshInterval: 1000, // 默认不会自动请求  每隔xx毫秒,自动请求接口,获取最新数据。
  revalidateOnFocus: false, // 默认true  当你重新聚焦一个页面或在标签页之间切换时,SWR 会自动重新请求数据 。
  revalidateOnReconnect: true, // 默认true  网络恢复时,是否自动重新请求 。

  // 以下配置(useSWRInfinite永不生效 ,useSWRInfinite会保留页面离开前的数据和请求页数)
  revalidateIfStale: true, // 默认true 切换路由,返回后是否使用缓存,若用缓存则不会重新调用接口 
};

useSWRInfinite 继承了useSWR 的配置选项,同时,拥有一些特殊属性。



//useSWRInfinite 的几个特殊属性说明:
export const useSWRInfiniteDefOption: SWRInfiniteConfiguration = {
  initialSize: 2, // 默认1 。初始加载的页数
  revalidateAll: false, // 默认为 false, 每次加载下一页,是否尝试重新请求已加载的所有页面
  persistSize: false, // 默认为 false: 当key([xx,params]) 发生变化时,是否将 page_size重置为 1 (或者 initialSize, 如果设置了该参数)
  revalidateFirstPage: false, // 默认为 true, 每次加载下一页,是否重新请求第一页。
  ...useSWRDefOption,
};

revalidateIfStale 在**useSWRInfinite** 中使用的特殊说明:useSWRInfinite会保留页面离开前的数据和请求页数

场景1:

revalidateIfStale:truerevalidateOnMount: true :即此页面是默认发起请求的。那么若此时从其他路由调跳回当路由,且查询参数没变的情况,那么useSWRInfinite会使用路由离开前的缓存的数据和请求页数,不再重新调用接口。(注:详情参考管理端V3用户页面)

场景2:

revalidateIfStale:truerevalidateOnMount: false :即此页面是调用mutate(),手动发起请求的。那么若此时从其他路由调跳回当路由,且查询参数没变的情况,虽然useSWRInfinite会缓存的离开前的数据和请求页数,但是由于 调用了mutate(),所以相当于强制刷新了,所以会重新调用接口,同时还是会默认加载上次已加载的页数。(注:详情参考管理端V3客户列表页面)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值