1

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 Answers3

1

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} />}
0

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.

A-Tech
  • 806
  • 6
  • 22
  • Thanks for your help. It didn't solve my problem unfortunately. I just found out that the line in my App is completely irrelevant so I deleted it entirely. At least I could see in my dev-tools that the todo items will get saved under the key "todos-{number of the day, e.g. 1}". – twelve.steven Feb 24 '23 at 13:48
  • Glad I could be of (some) help :) If you edit your code please edit the post accordingly, otherwise others can't really help you. – A-Tech Feb 24 '23 at 13:54
0

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... :)

juxhin bleta
  • 342
  • 2
  • 5
  • 16