Home > Net >  react.js event handler is not returning updated data
react.js event handler is not returning updated data

Time:09-27

I am new to react.js. I am following the video on youtube for learning react.js. I am working on simple event handling and stuck in some issue. I want to check/uncheck the checkbox when user performs onclick function on the checkbox. but somehow the returning array is not updated and checkbox is not actionable. I am pasting my code below:

App.js

import React from 'react'
import Header from './components/Header'
import Todolist from './components/Todolist'
import todosData from './data/todosData'

class App extends React.Component {

  constructor(){
    super()
    this.state = {
      todos: todosData
    }
    this.handleChange = this.handleChange.bind(this)
  }

  handleChange(id){
    this.setState(prevState => {
        console.log(prevState.todos)
        const updatedTodos = prevState.todos.map(todo => {
          if(todo.id === id){
            todo.completed = !todo.completed
          }
          return todo
        })
        console.log(updatedTodos)
        return{
          todos : updatedTodos
        }
    })
  }

  render(){
    const todoItems = this.state.todos.map(item => <Todolist key={item.id} item={item} handleChange={this.handleChange} />)
    return (
      <div>
        <Header />
        {todoItems}
      </div>
    )
  }

}

export default App;

TodoList.js

import React from 'react'

function Todolist(props){
    return(
        <div className='todo-item'>
            <input type='checkbox'
                   checked={props.item.completed} 
                   onChange={() => props.handleChange(props.item.id)} 
            />
            <span>{props.item.text}</span>
        </div>
        )
}

export default Todolist

CodePudding user response:

You are trying to mutate the original item/object of the array at this line!

todo.completed = !todo.completed

You can try to create a new object using Object.assign or using spread. Both are fine but the spread way is preferable.

Using Object.assign

  handleChange(id) {
    this.setState((prevState) => {
      const updatedTodos = prevState.todos.map((todo) => {
        // Object.assign creates a new object!
        const changedTodo = Object.assign({}, todo, {
          completed: todo.id === id ? !todo.completed : todo.completed
        });

        return changedTodo;
      });
      return {
        todos: updatedTodos
      };
    });
  }

Using spread ...

handleChange(id) {
    this.setState((prevState) => {
      const updatedTodos = prevState.todos.map((todo) => {
        const changedTodo = {
          ...todo,
          completed: todo.id === id ? !todo.completed : todo.completed
        };

        return changedTodo;
      });
      return {
        todos: updatedTodos
      };
    });
  }

CodePudding user response:

As stated here, setState runs twice in strict mode. The state updater function gets called twice with the same input. Which should be fine and produce the same output. But the problem here is you have reused the todo object instead of creating a new one. Which in turn causes the completed flag to be flipped twice.

You can change your updater function to create a new todo object every time:

this.setState(prevState => {
  const updatedTodos = prevState.todos.map(todo => {
    if (todo.id === id) {
      return {
        ...todo,
        completed: !todo.completed,
      };
    }
    return todo;
  });
  return {
    todos: updatedTodos,
  };
});
  • Related