React 异步请求数据处理优化经验总结

问题背景

在开发打卡功能时,遇到了一个典型的异步请求数据处理问题:

  1. 双重处理任务打卡列表的异步请求获取数据后,又被 useEffect 重复处理
  2. 重复渲染:导致页面跳动,影响用户体验
  3. 状态管理复杂:时间变化时需要更新任务状态,但又要避免不必要的网络请求

问题分析

原始方案的问题

javascript 复制代码
const [currentTime, setCurrentTime] = useState('');
const [taskPunch, setTaskPunch] = useState([]);

// 时分秒
useEffect(() => {
// 每秒更新一次当前时间
    const updateTime = () => {
      try {
        const date = new Date();
        const hour = date.getHours();
        const minute = date.getMinutes();
        const second = date.getSeconds();
        const hourStr = hour < 10 ? `0${hour}` : hour;
        const minuteStr = minute < 10 ? `0${minute}` : minute;
        const secondStr = second < 10 ? `0${second}` : second;
        const currentTimeStr = `${hourStr}:${minuteStr}:${secondStr}`;
        setCurrentTime(currentTimeStr);
      } catch (error) {
        console.log('error', error);
      }
    };

    updateTime(); // 立即执行一次
    const interval = setInterval(updateTime, 1000);
    return () => clearInterval(interval);
}, []);

// 第一次处理:任务打卡异步请求获取数据
const getTaskPunch = async () => {
  const res = await http.post('/api/wx/getList');
  setTaskPunch(res.data); // 第一次渲染
};

// 第二次处理:useEffect 再次处理任务打卡的数据
useEffect(() => {
  const newTaskPunch = taskPunch.map(item => {
    // 处理逻辑
  });
  setTaskPunch(newTaskPunch); // 第二次渲染
}, [currentTime, taskPunch]);

问题

  • 异步请求的数据被处理了两次
  • 每次时间变化都会触发重新渲染
  • 用户体验差,有跳动感

解决方案

核心思路:分离关注点,避免重复处理异步数据

1. 抽取处理函数

javascript 复制代码
// 将异步数据处理逻辑抽取为独立函数
const processTaskStatus = useCallback((data, time) => {
  return data.map(item => {
    // 时间格式处理
    const beginTimePart = item.taskBeginTime.split(' ')[1];
    const endTimePart = item.taskEndTime.split(' ')[1];
    
    const beginTime = beginTimePart.length === 5 
      ? beginTimePart + ':00' 
      : beginTimePart;
    const endTime = endTimePart.length === 5 
      ? endTimePart + ':00' 
      : endTimePart;

    // 状态判断逻辑
    const isInRange = time >= beginTime && time <= endTime;
    
    let taskStatus = 'pending';
    if (item.taskCompanyStatus === 1) {
      taskStatus = 'completed';
    } else if (time > endTime) {
      taskStatus = 'timeout';
    } else if (isInRange) {
      taskStatus = 'available';
    }
    
    return { ...item, isInRange, taskStatus };
  });
}, []);

2. 异步请求时直接处理

javascript 复制代码
const getTaskPunch = useCallback(async () => {
  try {
    const res = await http.post('/api/wx/getList');
    if (res.code === 200) {
      // 异步请求后直接处理数据,避免后续重复处理
      const processedData = processTaskStatus(res.data, currentTime);
      setTaskPunch(processedData);
    }
  } catch (error) {
    console.log('error', error);
  }
}, [currentTime, processTaskStatus]);

3. 时间变化时更新状态

javascript 复制代码
useEffect(() => {
  if (taskPunch.length > 0 && currentTime) {
    // 使用现有异步数据重新处理状态
    const updatedData = processTaskStatus(taskPunch, currentTime);
    
    // 检查是否有状态变化,避免不必要的更新
    const hasChanges = updatedData.some((item, index) => {
      const currentItem = taskPunch[index];
      return currentItem && (
        item.isInRange !== currentItem.isInRange ||
        item.taskStatus !== currentItem.taskStatus
      );
    });
    
    if (hasChanges) {
      setTaskPunch(updatedData);
    }
  }
}, [currentTime, taskPunch, processTaskStatus]);

关键优化点

1. 异步数据处理函数抽取

  • 将复杂的异步数据处理逻辑抽取为独立函数
  • 提高代码复用性和可维护性
  • 便于单元测试

2. 避免重复处理异步数据

  • 在异步请求获取数据时直接处理,避免后续 useEffect 重复处理
  • 减少不必要的状态更新

3. 条件更新异步数据

  • 只在真正有状态变化时才更新异步请求的数据
  • 避免相同数据导致的重复渲染

4. 异步请求依赖管理

  • 合理设置 useCallbackuseEffect 的依赖项
  • 避免循环依赖和无限渲染

性能提升效果

渲染次数对比

  • 优化前:每次时间变化 → 2次渲染(异步数据被处理两次)
  • 优化后:每次时间变化 → 1次渲染(只在有变化时)

用户体验

  • 优化前:页面跳动,视觉不连贯
  • 优化后:平滑过渡,状态变化自然

通用经验

1. 异步数据处理原则

javascript 复制代码
// ✅ 推荐:异步请求后直接处理
const fetchAndProcess = async () => {
  const res = await api.getData();
  const processedData = processData(res.data);
  setState(processedData);
};

// ❌ 避免:异步数据多次处理
const fetchData = async () => {
  const res = await api.getData();
  setState(res.data); // 第一次处理
};
useEffect(() => {
  const processed = processData(state); // 第二次处理
  setState(processed);
}, [state]);

2. 异步数据状态更新策略

javascript 复制代码
// ✅ 推荐:条件更新异步数据
if (hasChanges) {
  setState(newData);
}

// ❌ 避免:无条件更新异步数据
setState(newData);

3. 异步数据处理函数设计

javascript 复制代码
// ✅ 推荐:纯函数,易于测试
const processAsyncData = (data, params) => {
  return data.map(item => {
    // 处理逻辑
  });
};

// ❌ 避免:副作用函数
const processAsyncData = (data) => {
  setState(data); // 副作用
  return data;
};

总结

这个优化案例体现了 React 异步请求开发中的几个重要原则:

  1. 单一职责:每个函数只负责一个明确的任务
  2. 避免重复:异步数据处理逻辑只执行一次
  3. 条件更新:只在必要时更新异步数据状态
  4. 性能优先:减少不必要的渲染和计算

通过这样的优化,不仅解决了具体的异步数据处理问题,更重要的是提升了代码质量和用户体验。这种思路可以应用到其他类似的异步请求数据处理场景中。

相关推荐
Robbie丨Yang13 分钟前
CSS 工作原理
前端·css
酒渣16 分钟前
css动态样式
前端·css
转转技术团队41 分钟前
从“v我50”到“疯狂星期四”:HTTPS如何用47天寿命的证书挡住中间人
前端
zeqinjie1 小时前
Flutter 使用 AI Cursor 快速完成一个图表封装【提效】
前端·flutter
真上帝的左手1 小时前
24. 前端-js框架-Vue
前端·javascript·vue.js
3Katrina1 小时前
《Stitch的使用指南以及AI新开发模式杂谈》
前端
无羡仙1 小时前
按下回车后,网页是怎么“跳”出来的?
前端·node.js
喝拿铁写前端1 小时前
Vue 实战:构建灵活可维护的菜单系统
前端·vue.js·设计模式
ZzMemory1 小时前
一套通关CSS选择器,玩转元素定位
前端·css·面试
圆心角1 小时前
小米面挂了
前端·面试