skip to Main Content

I am trying to build a calendar web app with daily to-do lists. So when clicking on the link on any day, the user should be able to add and edit what to do for that day. But I would also like to show the total number of to-dos added for each day in the calendar page. E.g. 1 To-Do: "show total numbers of to-dos for this day"

I tried calling todos.length from localstorage, but it always shows 0.

App.jsx:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Calendar from './components/Calendar';
import TodoList from './components/TodoList';
import React, { useState } from 'react';

const App = () => {

  return (
    <div>
      <h1>Calendar + To-Do-List Project</h1>
      <Router>
        <Routes>
          <Route path="/" element={<Calendar />} />
          <Route path="/todo/:day" element={<TodoList />} />
        </Routes>
      </Router>
    </div>
  );
}

export default App;

Calendar.jsx:

import React from 'react';
import './calendar.css';
import moment from 'moment';
import { Link } from 'react-router-dom';

const Day = ({ number, id }) => {
  const [todos, setTodos] = React.useState(() => JSON.parse(localStorage.getItem(`todos-${id}`)) || []);
  return (
    <div className="day">
      {number} <Link to={`/todo/${number}`}> To-Do: {todos.length} </Link>
    </div>
  );
};

const Calendar = () => {
  const now = moment();
  const daysInMonth = now.daysInMonth();
  const monthStart = now.startOf('month').day() === 0 ? 7 : now.startOf('month').day();
  const weeks = [];

  let week = [];
  for (let i = 1; i < monthStart; i++) {
    week.push(null);
  }
  for (let i = 1; i <= daysInMonth; i++) {
    week.push(i);
    if (week.length === 7) {
      weeks.push(week);
      week = [];
    }
  }
  if (week.length > 0) {
    while (week.length < 7) {
      week.push(null);
    }
    weeks.push(week);
  }

  const dayNames = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];

  return (
    <div className="calendar-container">
      <div className="calendar-header">
        {now.format('MMMM YYYY')}
      </div>
      <div className="calendar-days">
        <div className="calendar-week">
          {dayNames.map((dayName) => (
            <div key={`day-${dayName}`} className="calendar-day-name">
              {dayName}
            </div>
          ))}
        </div>
        {weeks.map((week, index) => (
          <div key={`week-${index}`} className="calendar-week">
            {week.map((day) => (
              <div key={`day-${day}`} className="calendar-day">
                {day && <Day number={day} id={day} />}
              </div>
            ))}
          </div>
        ))}
      </div>
    </div>
  );
};


export default Calendar;

ToDoList.jsx:

import React, { useState, useEffect } from 'react';
import './TodoList.css';
import { useParams } from 'react-router-dom';
import { NavLink } from "react-router-dom";

const TodoList = () => {
  const {day} = useParams();
  const [todos, setTodos] = useState(() => JSON.parse(localStorage.getItem(`todos-${day}`)) || []);
  const [todo, setTodo] = useState("");
  const [todoEditing, setTodoEdit] = useState(null);
  const [editingText, setEditText] = useState("");

  useEffect(() => {
    const json = JSON.stringify(todos);
    localStorage.setItem(`todos-${day}`, json);
  }, [todos, day]);

  const addTodo = (e) => {
    e.preventDefault();
    const newTodo = {
      id: new Date().getTime(),
      text: todo,
      completed: false,
    };
    setTodos([...todos].concat(newTodo));
    setTodo("");
  };

  const toggleCompleted = (id) => {
    let updatedTodos = [...todos].map((todo) => {
      if (todo.id === id) {
        todo.completed = !todo.completed;
      }
      return todo;
    });
    setTodos(updatedTodos);
  };

  const deleteTodo = (id) => {
    let updatedTodos = [...todos].filter((todo) => todo.id !== id);
    setTodos(updatedTodos);
  };

  const submitEdits = (id) => {
    const updatedTodos = [...todos].map((todo) => {
      if (todo.id === id) {
        todo.text = editingText;
      }
      return todo;
    });
    setTodos(updatedTodos);
    setTodoEdit(null);
  };

  return (
    <div className="todo-list-container">
      <div align='right'>
        <NavLink to='/' style={{textDecoration: 'none', color: 'black'}}><button > Back to Calendar </button></NavLink>
      </div>
      <h2 className="todo-list-header">To-Dos for Today</h2>        
      <form onSubmit={addTodo}>
        <input
          type="text"
          value={todo}
          onChange={(e) => setTodo(e.target.value)}
        />
        <button>Add</button>
      </form>
      <ul className="todo-list">
      {todos.map((todo) => (
          <div key={todo.id} >
            <div >
              <input
                type="checkbox"
                id="completed"
                checked={todo.completed}
                onChange={() => toggleCompleted(todo.id)}
              />
              {todo.id === todoEditing ? (
                <input
                  type="text"
                  onChange={(e) => setEditText(e.target.value)}
                />
              ) : (
                <div><span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>{todo.text}</span></div>
              )}
            </div>
            <div >
              {todo.id === todoEditing ? (
                <button onClick={() => submitEdits(todo.id)}>Submit</button>
              ) : (
                <button onClick={() => setTodoEdit(todo.id)}>Edit</button>
              )}
              <button onClick={() => deleteTodo(todo.id)}>Delete</button>
            </div>
          </div>
        ))}
      </ul>
    </div>
  );
}

export default TodoList;

3

Answers


  1. Chosen as BEST ANSWER

    Thank you for your answers.

    The solution to my problem was to edit the return statement of my Calendar.jsx.

    Before:

    {day && <Day number={day} id={now.set('date', day).format('YYYY-MM-DD')} />}
    

    After:

    {day && <Day number={day} id={day} />}
    

  2. You set your localStorage item with these IDs: todos-${id} but in your App you get them via JSON.parse(localStorage.getItem('todos').

    Hence, the ID is not the same, there can’t be any data. If you check your site using the dev-tools (F12) you can actually see the contents of your localStorage by item.

    Login or Signup to reply.
  3. first of all you don’t need to return a function as default value for todos state, JSON.parse() returns a string.

    todos state into your App.jsx is also redundant.

    Your issue is at Day component, your are pushing to your other route the day while trying to get the item from localStorage with the id.

    Hope it helps… 🙂

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search