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
Thank you for your answers.
The solution to my problem was to edit the return statement of my Calendar.jsx.
Before:
After:
You set your localStorage item with these IDs:
todos-${id}
but in your App you get them viaJSON.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.
first of all you don’t need to return a function as default value for
todos
state,JSON.parse()
returns astring
.todos
state into yourApp.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 fromlocalStorage
with theid
.Hope it helps… 🙂