Horje
Build a Habit Tracker App with React and local storage

In this article, we will be Building a Habit Tracker App with React and local storage to persist habit data. The habit tracker app is a web application that helps users track their daily habits and progress over time. It provides a simple and intuitive interface for users to add new habits with descriptions, edit, remove, and mark them as completed each day and the app will display a summary of their progress for each habit.

Preview Output: Let us have a look at how the final output will look like.

Screenshot-(479)

Prerequisites:

Approach to build a Habit Tracker App with React and Local Storage:

  • Habit tracker involves creating UI that enables user to add, edit, remove and track the progress of habits.
  • We will Use React Hooks (useState, useEffect) to manage state for habits. Use localStorage to persist the habits data.
  • We will be splitting the code into components so codebase remains structured.
  • Application will start with adding new habits, once user add new habits they can edit and remove the habits and can see the weekly progress of habit.
  • User can mark the habits as done or undone.

Steps to Create a React App & Install Required Modules:

Step 1: Create a new React JS project using the following command

npx create-react-app habit-tracker

Step 2: Navigate to project directory

cd habit-tracker

Step 3: Install the require packages in your application using the following command.

npm i bootstrap
npm install react-icons --save

Project structure:

Screenshot-(464)

The updated dependencies in package.json will look like this:

"dependencies": {
"bootstrap": "^5.3.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^5.0.1",
"react-router": "^6.22.1",
"react-router-dom": "^6.22.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Example: Write the following code in respective files to build Habit Tracker App

JavaScript
// App.js
import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import NavBar from './Components/NavBar';
import AddHabit from './Components/AddHabit';
import ViewWeekly from './Components/ViewWeekly';

const App = () => {

return (
    <>
    <NavBar />
    <div className="container mt-5">
        <Routes>
        <Route path="/" element={<AddHabit />} />
        <Route path="/view-weekly" element={<ViewWeekly />} />
        </Routes>
    </div>
    </>
);
};

export default App;
JavaScript
// NavBar.js
import React from 'react';
import { Link } from 'react-router-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
const Navbar = () => {
    return (
        <nav className="navbar navbar-expand-lg
    navbar-dark bg-success">
            <div className="container">
                <Link className="navbar-brand"
                    to="/">Habit Tracker
                </Link>
                <div className="collapse navbar-collapse">
                    <ul className="navbar-nav mr-auto">
                        <li className="nav-item">
                            <Link className="nav-link"
                                to="/">Add New Habit
                            </Link>
                        </li>
                        <li className="nav-item">
                            <Link className="nav-link"
                                to="/view-weekly">View Weekly
                            </Link>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    );
};

export default Navbar;
JavaScript
// AddHabit.js

import React, { useState, useEffect } from 'react';
import { AiOutlineDelete, AiOutlineEdit } from 'react-icons/ai';
import 'bootstrap/dist/css/bootstrap.min.css';

const AddHabit = () => {
// Load initial habits from localStorage or use an empty array if none exist
const initialHabits = JSON.parse(localStorage.getItem('habits')) || [];

const [habits, setHabits] = useState(initialHabits);
const [habit, setHabit] = useState('');
const [description, setDescription] = useState('');
const [editIndex, setEditIndex] = useState(null);

useEffect(() => {
    // Save habits to localStorage whenever they change
    localStorage.setItem('habits', JSON.stringify(habits));
}, [habits]);

const handleSubmit = (e) => {
    e.preventDefault();
    if (!habit.trim()) {
    alert('Habit name cannot be empty');
    return;
    }
    if (editIndex !== null) {
    const updatedHabits = [...habits];
    updatedHabits[editIndex].name = habit;
    updatedHabits[editIndex].description = description;
    setHabits(updatedHabits);
    setEditIndex(null);
    } else {
    setHabits([...habits, { name: habit, description, status: { Monday: false, Tuesday: false, Wednesday: false, Thursday: false, Friday: false, Saturday: false, Sunday: false } }]);
    }
    setHabit('');
    setDescription('');
};

const removeHabit = (index) => {
    const updatedHabits = habits.filter((_, i) => i !== index);
    setHabits(updatedHabits);
};

const editHabit = (index) => {
    setHabit(habits[index].name);
    setDescription(habits[index].description || ''); // Handle case where description may be undefined
    setEditIndex(index);
};

return (
    <div>
    <h3 className='text-success'>GeekForGeeks Habit Tracker</h3>
    <h4>Add New Habit</h4>
    <form onSubmit={handleSubmit}>
        <div className="mb-3">
        <input
            type="text"
            className="form-control"
            placeholder="Enter habit name"
            value={habit}
            onChange={(e) => setHabit(e.target.value)}
        />
        </div>
        <div className="mb-3">
        <input
            type="textarea"
            className="form-control"
            placeholder="Enter habit description"
            value={description}
            onChange={(e) => setDescription(e.target.value)}
        />
        </div>
        <button type="submit" className="btn btn-primary">
        {editIndex !== null ? 'Save' : 'Add'}
        </button>
    </form>
    <table className="table">
        <thead>
        <tr>
            <th>Index</th>
            <th>Habit</th>
            <th>Description</th>
            <th>Actions</th>
        </tr>
        </thead>
        <tbody>
        {habits.map((habit, index) => (
            <tr key={index}>
            <td>{index + 1}</td>
            <td>{habit.name}</td>
            <td>{habit.description}</td>
            <td>
                <button className="btn btn-primary me-2" onClick={() => editHabit(index)}>
                <AiOutlineEdit />
                </button>
                <button className="btn btn-danger" onClick={() => removeHabit(index)}>
                <AiOutlineDelete />
                </button>
            </td>
            </tr>
        ))}
        </tbody>
    </table>
    </div>
);
};

export default AddHabit;
JavaScript
// ViewWeekly.js

import React, { useState } from 'react';
import { FaCheck, FaTimes } from 'react-icons/fa';
import 'bootstrap/dist/css/bootstrap.min.css';

const ViewWeekly = () => {
const [habits, setHabits] = useState(() => {
    const storedHabits = localStorage.getItem('habits');
    return storedHabits ? JSON.parse(storedHabits) : [];
});

const toggleStatus = (habitIndex, day) => {
    const updatedHabits = [...habits];
    updatedHabits[habitIndex].status[day] = !updatedHabits[habitIndex].status[day];
    setHabits(updatedHabits);
    localStorage.setItem('habits', JSON.stringify(updatedHabits));
};

const getPreviousDates = () => {
    const currentDate = new Date();
    const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
    const previousDates = [];
    for (let i = 6; i >= 0; i--) {
    const prevDate = new Date(currentDate);
    prevDate.setDate(currentDate.getDate() - i);
    const dayOfWeek = days[prevDate.getDay()];
    const date = prevDate.getDate();
    const month = prevDate.getMonth() + 1;
    previousDates.push({ dayOfWeek, date, month });
    }
    return previousDates;
};

const previousDates = getPreviousDates();

return (
    <div>
    <h4>Weekly Habits Progress:</h4>
    <table className="table table-bordered mt-5">
        <thead>
        <tr>
            <th className="bg-success text-white">Habit</th>
            {previousDates.map((date, index) => (
            <th className="bg-success text-white" key={index}>
                {date.dayOfWeek} - {date.date}/{date.month}
            </th>
            ))}
        </tr>
        </thead>
        <tbody>
        {habits.map((habit, habitIndex) => (
            <tr key={habitIndex}>
            <td>
                <h4>{habit.name}</h4>
                <small>{habit.description}</small>
            </td>
            {Object.keys(habit.status).map((day) => (
                <td key={day} className="text-center" style={{ cursor: 'pointer' }} onClick={() => toggleStatus(habitIndex, day)}>
                {habit.status[day] ? (
                    <FaCheck className="text-success" title="Mark undone" size={40} />
                ) : (
                    <FaTimes className="text-danger" title="Mark Done" size={40} />
                )}
                </td>
            ))}
            </tr>
        ))}
        </tbody>
    </table>
    </div>
);
};

export default ViewWeekly;

Start your application using the following command.

npm start

Output: Open web-browser and type the following URL http://localhost:3000/




Reffered: https://www.geeksforgeeks.org


Dev Scripter

Related
How To Dockerize Maven Project? And How Many Ways To Accomplish It? How To Dockerize Maven Project? And How Many Ways To Accomplish It?
How To Clone Private Git Repo With Dockerfile? How To Clone Private Git Repo With Dockerfile?
How To Use Docker For IoT Applications? How To Use Docker For IoT Applications?
Create a Quiz App with Next JS Create a Quiz App with Next JS
Creating a Travel Journal App using React Creating a Travel Journal App using React

Type:
Geek
Category:
Coding
Sub Category:
Tutorial
Uploaded by:
Admin
Views:
12