问题背景
在开发打卡功能时,遇到了一个典型的异步请求数据处理问题:
- 双重处理 :
任务打卡
列表的异步请求获取数据后,又被useEffect
重复处理 - 重复渲染:导致页面跳动,影响用户体验
- 状态管理复杂:时间变化时需要更新任务状态,但又要避免不必要的网络请求

问题分析
原始方案的问题
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. 异步请求依赖管理
- 合理设置
useCallback
和useEffect
的依赖项 - 避免循环依赖和无限渲染
性能提升效果
渲染次数对比
- 优化前:每次时间变化 → 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 异步请求开发中的几个重要原则:
- 单一职责:每个函数只负责一个明确的任务
- 避免重复:异步数据处理逻辑只执行一次
- 条件更新:只在必要时更新异步数据状态
- 性能优先:减少不必要的渲染和计算
通过这样的优化,不仅解决了具体的异步数据处理问题,更重要的是提升了代码质量和用户体验。这种思路可以应用到其他类似的异步请求数据处理场景中。