You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 19:51:13