I'm just refreshing myself on functional components and react state hooks, building a simple react todo list app- all the simple functionalities are built out but I have this one bug during initial state where there is an empty task rendering in the list. What am I missing? Any help would be greatly appreciated. :)
App.js:
import TodoList from './TodoList'
function App() {
return (
<div>
<TodoList />
</div>
);
}
export default App;
Todolist.js:
import React, {useState} from 'react'
import NewTodoForm from './NewTodoForm'
import Todo from './Todo'
const TodoList = () => {
const [state, setState] = useState({
list: [{title: "", id: ""}]
})
const addTodo = (newTodo) => {
setState({
list: [...state.list, newTodo]
})
console.log('after state change in addtodo', state.list.title)
}
const remove = (toDoId) => {
console.log('logging remove')
setState({
list: state.list.filter(todo => todo.id !== toDoId)
})
}
const strike = e => {
const element = e.target;
element.classList.toggle("strike");
}
const update = (id, updatedTask) => {
//i cant mutate state directly so i need to make a new array and set that new array in the state
const updatedTodos = state.list.map(todo => {
if (todo.id === id) { // find the relevant task first by mapping through existing in state and add updated info before storing it in updatedtodos
return { ...todo, title: updatedTask}
}
return todo
})
console.log('updated todos', updatedTodos)
setState({
list: updatedTodos
})
console.log('list after updating state')
}
return (
<div className="TodoList">
<h1>Todo List<span>A simple react app</span></h1>
<NewTodoForm addingTodo={addTodo}/>
{ state.list.map(todo => <Todo id={todo.id} key={todo.id} title={todo.title} updateTodo={update} strikeThrough={strike} removeTodo={() => remove(todo.id)} />) }
</div>
)
}
export default TodoList
Todo.js:
import React, {useState} from 'react'
const Todo = ({id, title, removeTodo, strikeThrough, updateTodo}) => {
const [state, setState] = useState({
isEditing: false,
})
const [task, setTask] = useState(title);
const handleUpdate = (e) => {
e.preventDefault()
updateTodo(id, task)
setState({ isEditing: false})
}
const updateChange = (e) => {
// setState({...state, [e.target.name]: e.target.value})
setTask(e.target.value)
console.log(task)
}
return (
<div>
{state.isEditing ?
<div className="Todo">
<form className="Todo-edit-form" onSubmit={handleUpdate}>
<input
type="text"
value={task}
name="task"
onChange={updateChange}
>
</input>
<button>Submit edit</button>
</form>
</div> :
<div className="Todo">
<ul>
<li className="Todo-task" onClick={strikeThrough}>{title}</li>
</ul>
<div className="Todo-buttons">
<button onClick={() => setState({isEditing: !state.isEditing})}><i class='fas fa-pen' /></button>
<button onClick={removeTodo}><i class='fas fa-trash' /></button>
</div>
</div>
}
</div>
)
}
export default Todo
CodePudding user response:
You're rendering your to-do's with:
{ state.list.map(todo => <Todo id={todo.id} key={todo.id} title={todo.title} updateTodo={update} strikeThrough={strike} removeTodo={() => remove(todo.id)} />) }
Your initial state is:
{ list: [{title: "", id: ""}] }
The above state will cause React to render an empty to-do item for you. Once you clear the array, you should not see anything. Another option is to change your rendering and add a conditional that checks if to-do item values are empty, to not render them.
CodePudding user response:
The initial state in TodoList seems to be having list:[{title: "", id: ""}], which contains empty title and id. Since it's mapped to create Todo, I think it starts with an empty Todo.