切换不同 tab 数据显示错乱问题

211 阅读1分钟

1. 问题描述

image.png

在上述图片中,由于本项目不同 tab 使用的同一个 redux store,所以每次切换 tab 时,如果在当前 tab 中请求的数据时间比较长,在此期间,又切换到另外一个标签页,那么这种情况上一次请求获取的数据就展示在当前页面

2. 解决方案

2.1 根据时间戳判断两次请求是否为同一次请求

effecs: {
  async query() {
    const currentTimeStamp = new Date().getTime()
    sessionStorage.setItem('appViewTimeStamp', currentTimeStamp)
    let res = await requestAPI()
    const sessionTimeStamp = sessionStorage.getItem('appViewTimeStamp')
    if (Number(sessionTimeStamp) === currentTimeStamp) {
      this.save({ data: res.data })
    }
  }
}

2.2 撤回请求

2.2.1 AbortController

import React, { useEffect, useState } from 'react';

const FetchDataComponent = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    // 创建一个 AbortController 实例
    const controller = new AbortController();
    const signal = controller.signal;

    // 发起网络请求
    fetch('https://api.example.com/data', { signal })
      .then(response => response.json())
      .then(data => setData(data))
      .catch(error => {
        if (error.name === 'AbortError') {
          console.log('请求被取消');
        } else {
          console.error('请求失败', error);
        }
      });

    // 在组件卸载时执行清理函数
    return () => {
      controller.abort(); // 取消请求
    };
  }, []); // 空依赖数组表示只在挂载和卸载时运行

  return (
    <div>
      {data ? (
        <div>{JSON.stringify(data)}</div>
      ) : (
        <div>加载中...</div>
      )}
    </div>
  );
};

export default FetchDataComponent;

2.2.2 Axios Cancel Token

import React, { useEffect, useState } from 'react';
import axios from 'axios';

const FetchDataComponent = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    const source = axios.CancelToken.source();

    // 发起网络请求
    axios.get('https://api.example.com/data', {
      cancelToken: source.token,
    })
      .then(response => setData(response.data))
      .catch(error => {
        if (axios.isCancel(error)) {
          console.log('请求被取消');
        } else {
          console.error('请求失败', error);
        }
      });

    // 在组件卸载时执行清理函数
    return () => {
      source.cancel('组件卸载,请求取消');
    };
  }, []);

  return (
    <div>
      {data ? (
        <div>{JSON.stringify(data)}</div>
      ) : (
        <div>加载中...</div>
      )}
    </div>
  );
};

export default FetchDataComponent;

2.3 使用第三方库(如 react-queryswr

这样的库内置了管理请求状态和缓存的功能,并且可以更容易地处理请求取消和组件卸载等场景。

import React from 'react';
import { useQuery } from 'react-query';

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data');
  if (!response.ok) {
    throw new Error('网络响应错误');
  }
  return response.json();
};

const FetchDataComponent = () => {
  const { data, error, isLoading } = useQuery('fetchData', fetchData, {
    staleTime: 5000, // 5 秒内数据保持新鲜
  });

  if (isLoading) return <div>加载中...</div>;
  if (error) return <div>请求失败: {error.message}</div>;

  return (
    <div>
      {data && <div>{JSON.stringify(data)}</div>}
    </div>
  );
};

export default FetchDataComponent;