I am trying to deploy the react app on netlify. Locally, my react todo app is working fine and local storage is persisting the data as well (locally). but when I deployed the web app on netlify, after visiting the live link it's showing me this error: "Unexpected token u in JSON at position 0 at JSON.parse" as it is retrieving the undefined. Solutions I tried: checked the typo 2: checked the object in the console which is displaying the data correctly. 3: i also kept the getTodoFromLocal function inside App() function and kept initial state of const [todos, setTodos] = useState([]); (to empty array) but this is not persisting data on page reloads
my code App.js
import React, {useEffect, useState} from 'react';
import './App.css';
import { Header, Form, TodoList } from './components';
// get data from local
const getTodoFromLocal = () => {
if(localStorage.getItem("todos") === null){
localStorage.setItem("todos", JSON.stringify([]));
} else {
try{
let localTodo = localStorage.getItem("todos");
let parsedTodo = JSON.parse(localTodo)
return parsedTodo;
} catch(error) {
console.log(error);
}
}
}
const App = () => {
// states
const [inputText, setInputText] = useState("");
const [todos, setTodos] = useState(getTodoFromLocal());
const [status, setStatus] = useState("all");
const [filteredTodo, setFilteredTodo] = useState([]);
// run this only once when app loads
useEffect(() => {
getTodoFromLocal();
}, [])
// Run once when app loads and every time when there is any change in todos ot status
useEffect(() => {
filterHandler();
saveToLocal();
}, [todos, status])
// functions
const filterHandler = () =>{
switch (status) {
case 'completed':
setFilteredTodo(todos.filter(todo => todo.completed === true));
break;
case 'incompleted':
setFilteredTodo(todos.filter(todo => todo.completed === false));
break;
default:
setFilteredTodo(todos);
break;
}
}
// save to local storage / set todos;
const saveToLocal = () => {
localStorage.setItem("todos", JSON.stringify(todos));
};
return (
<div className="App">
<Header />
<Form inputText = {inputText}
setInputText={setInputText}
todos={todos}
setTodos={setTodos}
setStatus={setStatus}
/>
<TodoList
todos={todos}
setTodos={setTodos}
filteredTodo={filteredTodo}
/>
</div>
);
}
export default App;
TodoList.js
import React from 'react';
import TodoItem from './TodoItem';
const TodoList = ({todos, setTodos, filteredTodo}) => {
return (
<div className='todo-container'>
<ul className='todo-list'>
{filteredTodo && filteredTodo.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
todos={todos}
setTodos={setTodos}
text={todo.text}
/>
))}
</ul>
</div>
)
}
export default TodoList;
Form.js
import React from 'react';
import './form.css';
import { v4 as uuidv4 } from 'uuid';
function Form({inputText, setInputText, todos, setTodos, setStatus}) {
const inputHandler = (e) => {
setInputText(e.target.value);
}
const submitHandler = (e) =>{
e.preventDefault();
// generate unique id for todo lists.
const uniqueId = uuidv4();
//add todo object on click of button
const addItem = !inputText ? alert("enter somthing") : setTodos([
...todos, {id: uniqueId, text: inputText, completed: false }
]);
//reset the input field after adding todo
setInputText("");
return addItem;
}
// filtered todo
const statusTodo = (e) => {
setStatus(e.target.value);
}
return (
<form>
<input type="text" className="todo-input" onChange={inputHandler} value={inputText}/>
<button className="todo-button" type="submit" onClick={submitHandler}>
<i className="fas fa-plus-square"></i>
</button>
<div className="select">
<select onChange={statusTodo} name="todos" className="filter-todo">
<option value="all">All</option>
<option value="completed">Completed</option>
<option value="incompleted">Incompleted</option>
</select>
<span><i className="fas fa-chevron-down"></i></span>
</div>
</form>
)
}
export default Form;
TodoItem.js
import React from 'react';
import './todo.css';
const TodoItem = ({text, todo, todos, setTodos}) => {
//delete an item;
const deleteHandler = () => {
setTodos(todos.filter(el => el.id !== todo.id))
}
const completeHandler = () => {
setTodos(todos.map((item )=> {
if(item.id === todo.id){
return {
...item, completed: !todo.completed
}
}
return item;
}));
}
return (
<div className='todo'>
<li className={`todo-item ${todo.completed ? 'completed' : "" }`}>{text}</li>
<button className='complete-btn' onClick={completeHandler}>
<i className='fas fa-check'></i>
</button>
<button className='trash-btn' onClick={deleteHandler}>
<i className='fas fa-trash'></i>
</button>
</div>
)
}
export default TodoItem;
Live link: https://clinquant-parfait-ceab31.netlify.app/
Github link: https://github.com/Mehreen57/Todo-app-react
I hope I have explained the issue. I will be thankful to all of you who can help me in this regard.
Thank you in advance.
CodePudding user response:
I figured the error. Going step by step.
You have
getTodoFromLocal
which is called when you setTodosconst [todos, setTodos] = useState(getTodoFromLocal());
here.As
localStorage.getItem("todos")
is null, you settodos
to[]
but do not return anything which returns undefined and value of todos is changed toundefined
.Then you have the function
saveToLocal
in which you storetodos
inlocalStorage
.which is called in useEffect whenever todos change.Todos changed to
undefined
> useEffect is called > insaveToLocal
function todos(undefined) is stored in localStorage.
updated code:
if (localStorage.getItem("todos") === null) {
localStorage.setItem("todos", JSON.stringify([]));
return []
}