Search code examples
reactjsasp.netasp.net-coreasp.net-mvc-4asp.net-web-api

how to persist the state in react with local storage and useeffect?


i'm a beginner in react. i have a simple time tracking app. it has a simple interface.

the problem is i have list of all employees which i am getting from the backend api in asp.net core. when i click on the employee there is screen with the checkin and checkout button where i can checkin and checkout my time. time is shown in the end. but i want persist it there

i am handling condition. when i checkin for first time the checkout button will enable I can do the checkout. by the time when i press checkout, the checkout time is noted and checkout button is disabled and checkin is enabled. when i checkin and i move to checkout i refresh or go to back button it will not persist in my state. i want to make that happen.

how to persist my button state with local storage? i have tried but nothing is working with local storage. it cannot stored my state. i want to persist my state like when i checkin or checkout or go backbutton and then came it will show me or select the latest recent button.

how put the work in the employeetimetracking.js?

import React, { useEffect, useState } from 'react';
import { Link, useParams } from 'react-router-dom';
import { fetchEmployeeById,checkIn,checkOut,fetchEmployeeStatus} from '../api/Employeeapi'; // Import your fetchEmployeeById method

function EmployeeTimeTracking() {
    const { employeeId } = useParams(); // Get the employee ID from the URL
    const [employeeName, setEmployeeName] = useState("Employee"); // Default name
    const [status, setStatus] = useState('checkOut');
    const [checkInTime, setCheckInTime] = useState(null);
    const [checkOutTime, setCheckOutTime] = useState(null);
    const [timeTrackingId, setTimeTrackingId] = useState(null); // To store the check-in ID
    // set the message for to display success and error msg
    //const[message,setmessage]=useState("");

    useEffect(() => {
        // Fetch employee data by ID
        const fetchEmployee = async () => {
            try {
                const employee = await fetchEmployeeById(employeeId); // Call your fetchEmployeeById method
                setEmployeeName(employee.name); // Assuming `name` is the field in the response
            } catch (error) {
                console.error("Error fetching employee:", error);
            }
        };

        
            const fetchStatus = async () => {
              try {
                const data = await fetchEmployeeStatus(employeeId); // Call the fetchEmployeeStatus function
                setStatus(data.status);
                setCheckInTime(data.checkInTime ? new Date(data.checkInTime).toLocaleTimeString("en-US", { timeZone: "America/New_York" }) : null);
                setCheckOutTime(data.checkOutTime ? new Date(data.checkOutTime).toLocaleTimeString("en-US", { timeZone: "America/New_York" }) : null);   
            } catch (error) {
                console.error("Error fetching employee status:", error);
              }
            };
        
        fetchStatus();
        fetchEmployee();
     
    }, [employeeId]);

    // Check-in function
    const handleCheckIn = async () => {
        try {
            const data = await checkIn(employeeId);
            setTimeTrackingId(data.id); // Store the ID of the check-in record
            setCheckInTime(new Date().toLocaleTimeString("en-US", { timeZone: "America/New_York" }));
            setStatus('checkedIn'); 

        } catch (error) {
            console.error("Check-in failed:", error);
        }
    };

    const handleCheckOut = async () => {
        try {
            await checkOut(timeTrackingId);
            setCheckOutTime(new Date().toLocaleTimeString("en-US", { timeZone: "America/New_York" }));
            setStatus('checkedOut'); 
        
        } catch (error) {
            console.error("Check-out failed:", error);
       
        }
    };

    return (
        <div className="p-3 max-w-md mx-auto">
            {/* Back Button */}
            <Link to="/" className="text-blue-500 absolute left-2 top-0 hover:underline">
                &larr; Back to Employee List
            </Link>

            <div className="p-4 max-w-md mx-auto">
                <h2 className="text-2xl font-bold mb-4 text-center">
                    Time Tracking for {employeeName} {/* Display employee name */}
                
                </h2>
                <div className="flex flex-col space-y-4 items-center">
                    <button
                        onClick={handleCheckIn}
                        disabled={status === 'checkedIn'}
                        className={`px-4 py-2 w-full sm:w-1/2 rounded ${status === 'checkedIn' ? 'bg-gray-300 cursor-not-allowed' : 'bg-green-500 hover:bg-green-600'} text-white font-semibold transition`}
                    >
                        Check In
                    </button>

                    <button
                        onClick={handleCheckOut}
                        disabled={status !== 'checkedIn'}
                        className={`px-4 py-2 w-full sm:w-1/2 rounded ${status !== 'checkedIn' ? 'bg-gray-300 cursor-not-allowed' : 'bg-red-500 hover:bg-red-600'} text-white font-semibold transition`}
                    >
                        Check Out
                    </button>
                </div>

                {/* {message && (
                    <div className="text-center mb-4 p-2 rounded bg-green-100 text-green-700">
                        {message}
                    </div>
                )} */}

                <div className="mt-6 text-center">
                    <p className="text-lg">Check-In Time: {checkInTime || 'Not Checked In'}</p>
                    <p className="text-lg">Check-Out Time: {checkOutTime || 'Not Checked Out'}</p>
                </div>
            </div>
        </div>
    );
}

export default EmployeeTimeTracking;

EmployeeTimeTracking.js

Employeeapi.js

import axios from 'axios';

const API_URL = 'http://localhost:5049/api/timetraking';



// export const fetchEmployeeTimeTracking = async (employeeId) => {
//     const response = await axios.get(`${API_URL}/timetrackingid/${employeeId}`);
//     return response.data;
// };

// Fetch all employees
export const fetchEmployees = async () => {
    try {
        const response = await axios.get(`${API_URL}/employees`); // Make sure this matches your backend
        return response.data; // Return the data directly
    } catch (error) {
        console.error("Error fetching employees:", error);
        console.log(`API URL for employees: ${API_URL}/employees`);

        throw error; // Rethrow the error so it can be caught in the component
    }

};
export const fetchEmployeeById = async (id) => {
    try {
        const response = await axios.get(`${API_URL}/employees/${id}`); // Adjust the URL as needed
       // console.log("Employee ID from URL:", id); // Log the ID

       return response.data; 
    } catch (error) {
        console.error(`Error fetching employee with ID ${id}:`, error);
        throw error; // Rethrow the error for handling in the component
    }
};


export const checkIn = async (employeeId) => {
    try {
        const response = await axios.post(`${API_URL}/checkin`, {
            employeeId: employeeId,  // Pass any other required data here
        });
        return response.data;
    } catch (error) {
        console.error("Error during check-in:", error);
        throw error;
    }
};

// Check-out an employee
export const checkOut = async (timeTrackingId) => {
    try {
        const response = await axios.post(`${API_URL}/checkout/${timeTrackingId}`);
        return response.data;
    } catch (error) {
        console.error("Error during check-out:", error);
        throw error;
    }
};

// Fetch employee status by employee ID
export const fetchEmployeeStatus = async (employeeId) => {
    try {
        const response = await axios.get(`${API_URL}/status/${employeeId}`); // Adjusting to match your endpoint
        return response.data; // Return the data directly
    } catch (error) {
        console.error(`Error fetching status for employee ${employeeId}:`, error);
        throw error; // Rethrow the error for handling in the component
    }
};

Employeelist.js

import React, {useEffect, useState} from 'react';
import { Link } from 'react-router-dom';
import { fetchEmployees } from '../api/Employeeapi';

// const employees = [
//     { id: 1, name: 'Alice' },
//     { id: 2, name: 'Bob' },
//     { id: 3, name: 'Charlie' },
//     { id: 4, name: 'Leon' },
// ];
function EmployeeList() {
   
    const [employees, setEmployees] = useState([]);
    const [error, setError] = useState(null);

    useEffect(() => {
        const loadEmployees = async () => {
            try {
                const data = await fetchEmployees();
                setEmployees(data);
            } catch (err) {
                setError("Failed to load employees.");
            }
        };

        loadEmployees();
    }, []);

     if (error) {
        return <div>Error: {error}</div>;
    }

    return (
        <div className="p-4 max-w-2xl mx-auto">
            <h1 className="text-2xl font-bold mb-4 text-center">Employee List</h1>
            <div className="grid gap-4 grid-cols-1 sm:grid-cols-2">
                {employees.map(emp => (
                    <Link
                        key={emp.id}
                        to={`/employee/${emp.id}`}
                        
                        className="p-4 bg-blue-100 rounded shadow hover:bg-blue-200 transition"
                    >
                        <p className="text-lg font-semibold">{emp.name}</p>
                    </Link>
                ))}
            </div>
        </div>
    );
}

export default EmployeeList;

Solution

  • So you want to keep the state of your buttons even after refresh or page change. For this you need to use localStorage.

    I added a few blocks to save the state of your variables in localStorage instead of the state (which is lost when you change page).

    
    import React, { useEffect, useState } from 'react';
    import { Link, useParams } from 'react-router-dom';
    import { fetchEmployeeById, checkIn, checkOut, fetchEmployeeStatus } from '../api/Employeeapi';
    
    function EmployeeTimeTracking() {
        const { employeeId } = useParams();
        const [employeeName, setEmployeeName] = useState("Employee");
        const [status, setStatus] = useState('checkOut');
        const [checkInTime, setCheckInTime] = useState(null);
        const [checkOutTime, setCheckOutTime] = useState(null);
        const [timeTrackingId, setTimeTrackingId] = useState(null);
    
        useEffect(() => {
            // Fetch employee data by ID
            const fetchEmployee = async () => {
                try {
                    const employee = await fetchEmployeeById(employeeId);
                    setEmployeeName(employee.name);
                } catch (error) {
                    console.error("Error fetching employee:", error);
                }
            };
    
            // ADDED THIS: Load status from localStorage
            const savedStatus = localStorage.getItem(`status_${employeeId}`);
            const savedCheckInTime = localStorage.getItem(`checkInTime_${employeeId}`);
            const savedCheckOutTime = localStorage.getItem(`checkOutTime_${employeeId}`);
            const savedTimeTrackingId = localStorage.getItem(`timeTrackingId_${employeeId}`);
    
            if (savedStatus) setStatus(savedStatus);
            if (savedCheckInTime) setCheckInTime(savedCheckInTime);
            if (savedCheckOutTime) setCheckOutTime(savedCheckOutTime);
            if (savedTimeTrackingId) setTimeTrackingId(savedTimeTrackingId);
    
            // Fetch current status from server
            const fetchStatus = async () => {
                try {
                    const data = await fetchEmployeeStatus(employeeId);
                    setStatus(data.status);
                    setCheckInTime(data.checkInTime ? new Date(data.checkInTime).toLocaleTimeString("en-US", { timeZone: "America/New_York" }) : null);
                    setCheckOutTime(data.checkOutTime ? new Date(data.checkOutTime).toLocaleTimeString("en-US", { timeZone: "America/New_York" }) : null);
                    setTimeTrackingId(data.id);
    
                    // ADDED THIS: Store these values in localStorage for persistence
                    localStorage.setItem(`status_${employeeId}`, data.status);
                    if (data.checkInTime) localStorage.setItem(`checkInTime_${employeeId}`, data.checkInTime);
                    if (data.checkOutTime) localStorage.setItem(`checkOutTime_${employeeId}`, data.checkOutTime);
                    if (data.id) localStorage.setItem(`timeTrackingId_${employeeId}`, data.id);
                } catch (error) {
                    console.error("Error fetching employee status:", error);
                }
            };
    
            fetchStatus();
            fetchEmployee();
        }, [employeeId]);
    
        const handleCheckIn = async () => {
            try {
                const data = await checkIn(employeeId);
                const currentTime = new Date().toLocaleTimeString("en-US", { timeZone: "America/New_York" });
                setTimeTrackingId(data.id);
                setCheckInTime(currentTime);
                setStatus('checkedIn');
    
                // ADDED THIS: Save to localStorage
                localStorage.setItem(`status_${employeeId}`, 'checkedIn');
                localStorage.setItem(`checkInTime_${employeeId}`, currentTime);
                localStorage.setItem(`timeTrackingId_${employeeId}`, data.id);
            } catch (error) {
                console.error("Check-in failed:", error);
            }
        };
    
        const handleCheckOut = async () => {
            try {
                await checkOut(timeTrackingId);
                const currentTime = new Date().toLocaleTimeString("en-US", { timeZone: "America/New_York" });
                setCheckOutTime(currentTime);
                setStatus('checkedOut');
    
                // ADDED THIS: Update localStorage
                localStorage.setItem(`status_${employeeId}`, 'checkedOut');
                localStorage.setItem(`checkOutTime_${employeeId}`, currentTime);
            } catch (error) {
                console.error("Check-out failed:", error);
            }
        };
    
        return (
            <div className="p-3 max-w-md mx-auto">
                <Link to="/" className="text-blue-500 absolute left-2 top-0 hover:underline">
                    &larr; Back to Employee List
                </Link>
    
                <div className="p-4 max-w-md mx-auto">
                    <h2 className="text-2xl font-bold mb-4 text-center">
                        Time Tracking for {employeeName}
                    </h2>
                    <div className="flex flex-col space-y-4 items-center">
                        <button
                            onClick={handleCheckIn}
                            disabled={status === 'checkedIn'}
                            className={`px-4 py-2 w-full sm:w-1/2 rounded ${status === 'checkedIn' ? 'bg-gray-300 cursor-not-allowed' : 'bg-green-500 hover:bg-green-600'} text-white font-semibold transition`}
                        >
                            Check In
                        </button>
    
                        <button
                            onClick={handleCheckOut}
                            disabled={status !== 'checkedIn'}
                            className={`px-4 py-2 w-full sm:w-1/2 rounded ${status !== 'checkedIn' ? 'bg-gray-300 cursor-not-allowed' : 'bg-red-500 hover:bg-red-600'} text-white font-semibold transition`}
                        >
                            Check Out
                        </button>
                    </div>
    
                    <div className="mt-6 text-center">
                        <p className="text-lg">Check-In Time: {checkInTime || 'Not Checked In'}</p>
                        <p className="text-lg">Check-Out Time: {checkOutTime || 'Not Checked Out'}</p>
                    </div>
                </div>
            </div>
        );
    }
    
    export default EmployeeTimeTracking;
    

    Maybe you should consider splitting this component and make the buttons independent components. It could help you have a clearer view of everything.