Home > OS >  Why is my empty object in useState hook rendering?
Why is my empty object in useState hook rendering?

Time:01-02

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.

  • Related