React中如何用LocalStorage与useEffect持久化打卡状态?
员工打卡状态持久化实现方案
核心逻辑
前端用localStorage做临时状态缓存,同时和后端ASP.NET Core API同步数据——组件挂载时优先从后端拉取最新打卡状态,再结合localStorage做兜底,确保刷新/重新进入页面时状态不丢失。
修改EmployeeTimeTracking.js的具体步骤
1. 定义状态与初始化逻辑
用React Hooks定义核心状态,在组件挂载时先调用后端API获取员工最新打卡记录,同时读取localStorage的缓存数据做 fallback:
import { useState, useEffect } from 'react'; const EmployeeTimeTracking = ({ employeeId }) => { // 核心状态:是否已打卡、打卡/签退时间 const [isCheckedIn, setIsCheckedIn] = useState(false); const [checkInTime, setCheckInTime] = useState(null); const [checkOutTime, setCheckOutTime] = useState(null); // 组件挂载时初始化状态 useEffect(() => { // 先从后端API获取最新打卡状态 const fetchLatestTimeRecord = async () => { try { const response = await fetch(`/api/employees/${employeeId}/latest-time-record`); if (response.ok) { const record = await response.json(); // 更新状态 setIsCheckedIn(record.isCheckedIn); setCheckInTime(record.checkInTime); setCheckOutTime(record.checkOutTime); // 同步到localStorage saveStateToLocalStorage(employeeId, record); } else { // 后端无数据时,读取localStorage缓存 loadStateFromLocalStorage(employeeId); } } catch (error) { // 网络错误时,读取localStorage缓存 loadStateFromLocalStorage(employeeId); } }; fetchLatestTimeRecord(); }, [employeeId]); // 封装保存状态到localStorage的函数 const saveStateToLocalStorage = (empId, state) => { localStorage.setItem(`employee_${empId}_time_state`, JSON.stringify(state)); }; // 封装从localStorage读取状态的函数 const loadStateFromLocalStorage = (empId) => { const savedState = localStorage.getItem(`employee_${empId}_time_state`); if (savedState) { const parsedState = JSON.parse(savedState); setIsCheckedIn(parsedState.isCheckedIn); setCheckInTime(parsedState.checkInTime); setCheckOutTime(parsedState.checkOutTime); } };
2. 处理Check In/Check Out逻辑
点击按钮时,先调用后端API完成打卡/签退,成功后更新前端状态并同步到localStorage:
// Check In 点击事件 const handleCheckIn = async () => { try { const currentTime = new Date().toISOString(); const response = await fetch(`/api/employees/${employeeId}/check-in`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ checkInTime: currentTime }) }); if (response.ok) { const newRecord = { isCheckedIn: true, checkInTime: currentTime, checkOutTime: null }; setIsCheckedIn(true); setCheckInTime(currentTime); setCheckOutTime(null); saveStateToLocalStorage(employeeId, newRecord); } } catch (error) { console.error('Check In failed:', error); } }; // Check Out 点击事件 const handleCheckOut = async () => { try { const currentTime = new Date().toISOString(); const response = await fetch(`/api/employees/${employeeId}/check-out`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ checkOutTime: currentTime }) }); if (response.ok) { const newRecord = { isCheckedIn: false, checkInTime: checkInTime, checkOutTime: currentTime }; setIsCheckedIn(false); setCheckOutTime(currentTime); saveStateToLocalStorage(employeeId, newRecord); } } catch (error) { console.error('Check Out failed:', error); } };
3. 按钮状态控制
根据isCheckedIn和时间状态控制按钮禁用:
return ( <div className="time-tracking-container"> <h3>员工打卡页面</h3> <div className="time-display"> {checkInTime && <p>打卡时间: {new Date(checkInTime).toLocaleString()}</p>} {checkOutTime && <p>签退时间: {new Date(checkOutTime).toLocaleString()}</p>} </div> <div className="button-group"> <button onClick={handleCheckIn} disabled={isCheckedIn} > Check In </button> <button onClick={handleCheckOut} disabled={!isCheckedIn || checkOutTime !== null} > Check Out </button> </div> </div> ); }; export default EmployeeTimeTracking;
关键注意事项
- 后端API配合:需要新增三个接口:
GET /api/employees/{id}/latest-time-record(获取员工最新打卡记录)、POST /api/employees/{id}/check-in、POST /api/employees/{id}/check-out(处理打卡/签退) - LocalStorage序列化:因为
localStorage只能存字符串,所以必须用JSON.stringify和JSON.parse处理状态对象 - 数据优先级:优先使用后端返回的最新数据,
localStorage仅作为网络异常或后端无数据时的兜底方案,避免前端缓存与后端数据不一致 - 员工ID隔离:存储
localStorage时用employee_{id}_time_state作为键,避免不同员工的状态互相覆盖
内容的提问来源于stack exchange,提问作者Hamd Mahmood
相关产品推荐
相关产品推荐

