B
B
Bizki2020-10-27 04:57:13
JavaScript
Bizki, 2020-10-27 04:57:13

Why doesn't the state of a component change in React?

Hello, I don’t understand why the state of the checkboxes does not change when clicked, the most interesting thing is that if you replace todo.completed with todo.text and change the text, then the state will be changed, also if you add console.log("Clicked", id), then it will output the word Clicked and the id of the checkbox. I would be very happy if you help me figure it out.
Here is the parent component code:

import React, { Component } from 'react'
import TodoItem from "./TodoItem"
import todosData from "./todosData"

class App extends Component  {
  
    constructor() {
        super()
        this.state = {
            todos: todosData
        }
        this.handleChange = this.handleChange.bind(this)
    }
    
    handleChange(id) {
      this.setState(prevState => {
          const updatedTodos = prevState.todos.map(todo => {
              if (todo.id === id) {
                  todo.completed = !todo.completed
              }
              return todo
          })
          return {
              todos: updatedTodos
          }
      })
  }
    render() {
      const todoItems = this.state.todos.map(item => <TodoItem key={item.id} item={item} handleChange={this.handleChange}/>)        
        return (
            <div className="todo-list">
                {todoItems}
            </div>
        )    
    }
}

export default App

Here is the code for the child component:

import React from "react"

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

export default TodoItem


Here is the code for the JSON file where everything comes from:
const todosData = [
    {
        id: 1,
        text: "Take out the trash",
        completed: true
    },
    {
        id: 2,
        text: "Grocery shopping",
        completed: false
    },
    {
        id: 3,
        text: "Clean gecko tank",
        completed: false
    },
    {
        id: 4,
        text: "Mow lawn",
        completed: true
    },
    {
        id: 5,
        text: "Catch up on Arrested Development",
        completed: false
    }
]

export default todosData

Answer the question

In order to leave comments, you need to log in

2 answer(s)
D
Dmitry, 2020-10-27
@Bizki

The string
todo.completed = !todo.completed
is executed twice on each click, so first the value becomes false, then true again, while the text variable changes nicely. I'm a little ashamed, because I don't know exactly how the react works in this case, but there are two ways out:
1)

if (todo.id === id) return { ...todo, completed: !todo.completed }
return todo

Which is actually more correct, since every mutable element in react should change the reference when it changes.
2) Calculate new completed immediately
onChange={() => props.handleChange(props.item.id, !props.item.completed)}

and already in the function use the received value
handleChange(id, newCompleted) {
      this.setState(prevState => {
        
          const updatedTodos = prevState.todos.map(todo => {
              if (todo.id === id) {
                todo.completed = newCompleted 
              }
              return todo
          })
          return {
              todos: updatedTodos
          }
      })

A
Anvar Shakhmaev, 2020-10-27
@RxR

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

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question