ashil
ashil

Reputation: 97

my check box in react app is not changing state after clicking the check box

i am creating a todoapp with react , when i click the checkbox nothing happens . I'm not getting a solution to this problem . my codes are given below

App.js

import './App.css';
import React from 'react'
import TodoItem from './components/TodoItem'
import todosData from './components/TodoList'

class App extends React.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
            }
            console.log(todo)
            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;

TodoItem.js

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

TodoList.js

const todosData = [
    {
        id : 1,
        text : "Take Out the trash",
        completed : false
    },
    {
        id : 2,
        text : "Grocery shopping",
        completed : false
    },
    {
        id : 3,
        text : "Mow lawn",
        completed : false
    },
    {
        id : 4,
        text : "Clean toilet",
        completed : true
    },
]

export default todosData

i'm getting data from TodoList.js as a dictionary to mimic a rest api response, i don't know what is the problem ,i'm new to react ,is there a problem with my code

React Version : 17.0.1

Upvotes: 0

Views: 44

Answers (1)

Nithish
Nithish

Reputation: 6039

One problem that I can see in your code is in handleChange method of App component state is getting mutated in prevState.todos.map. i.e.,

handleChange(id) {
        this.setState(prevState => {
           const updatedTodos = prevState.todos.map(todo => {
            if (todo.id === id) {               
               todo.completed = !todo.completed  //--> Here the state is getting mutated.
            }
//...rest of the code

Here, even though you are using Array.map on todos array but each todo in the map will still have the same reference hence when doing todo.completed = !todo.completed the todos array in the state is getting updated. Which may lead to unpredictable issues.

So we can convert that handleChange method to like below

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

Below is the runnable snippet.

const todosData = [{id:1,text:"Take Out the trash",completed:false},{id:2,text:"Grocery shopping",completed:false},{id:3,text:"Mow lawn",completed:false},{id:4,text:"Clean toilet",completed:true}]

const {Component} = 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>
    )
} 

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 => {
            return {
              ...todo,
              ...(todo.id === id && {completed: !todo.completed})
            }
          }) 
          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> 
      
    );
  }

}
  

ReactDOM.render(<App />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>

<div id="react"></div>

Upvotes: 1

Related Questions