简体   繁体   中英

Checkbox doesn't change its value on click in React todo application

Please help me with this I don't understand exactly where I doing wrong. So when I click on the checkbox values are not changing(if it's by default true when I click on the click it should the false ). For that in onChange in todoList component I am calling handleClick function there I change the todo.completed value(basically toggling the values).

In App.js inside the handleClick method When do console.log(todo) before returning from the map function value is toggling fine, but it is not updated in the updatedTodo .

App.js

import TodosData from "./todoData";
import TodoList from "./todoList";
import "./styles.css";

class App extends Component {
  constructor() {
    super();
    this.state = {
      todos: TodosData
    }
    this.handleChange = this.handleChange.bind(this)
  }

  handleChange(id) {
    this.setState(prevState => {
      const updatedTodo = prevState.todos.map(todo => {
        // console.log(updatedTodo);
        if(todo.id === id) {
          console.log("before the opt "+todo.completed);
          todo.completed = !todo.completed
          console.log("after the opt "+todo.completed);
        }
        //console.log(todo);
        return todo;
      })
      console.log(updatedTodo);
      return {
        todos: updatedTodo
      }
    });
  }

  render() {

    const todoDataComponents = this.state.todos.map(item => {
      return <TodoList key = {item.id} item = {item} handleChange = {this.handleChange} />
    })
    return (
      <div className="todo-list">{todoDataComponents}</div>
    );
  }
}

export default App;

todoList.jsx



class TodoList extends Component {
    constructor(props) {
        super(props);
        this.state = {}
    }
    render() {
        // console.log(this.props)
        return (
            <div className="todo-item">
                <input
                    type="checkbox"
                    checked={this.props.item.completed}
                    onChange = {() => this.props.handleChange(this.props.item.id)}
                />
                <p>{this.props.item.text}</p>
            </div>
        );
    }
}

export default TodoList;

todoData.js

const TodosData = [
    { id: 1, text: "Coding", completed: true },
    { id: 2, text: "Exercise", completed: false },
    { id: 3, text: "Learning", completed: true },
    { id: 4, text: "Programming", completed: true },
    { id: 5, text: "inspire", completed: false },
    { id: 6, text: "motivation", completed: true }
];

export default TodosData;

I can't see any error in your handleChange() method, it should work fine. However, I've just updated some of your code which you can test below.

I changed the name of your TodoList as it's not really a list but an item. I also changed it to a functional component as it's only presentational, there is no need to have its own state. Instead of adding a p tag after the input , you should use a label to make it accessible.

I haven't really changed anything inside your handleChange() method, only removed the console.log s and it works as expected.

Update : You're using React.StrictMode , where React renders everything twice on dev. As your handleChange() runs twice, it sets the clicked todo's completed state twice, making it to set back to its original state. So if it's false on first render it sets to true on click, but it's rendered again and on the second one it's back to false . You won't notice it as it's pretty fast.

To avoid it, you need to avoid mutating anything. So I've updated your onChange handler, it returns a new object if the completed property is changed.

Feel free to run the code snippet below and click on the checkboxes or the item texts.

 const TodosData = [ { id: 1, text: 'Coding', completed: true }, { id: 2, text: 'Exercise', completed: false }, { id: 3, text: 'Learning', completed: true }, { id: 4, text: 'Programming', completed: true }, { id: 5, text: 'Inspire', completed: false }, { id: 6, text: 'Motivation', completed: true }, ]; function TodoItem(props) { const { handleChange, item } = props; return ( <div className="todo-item"> <input type="checkbox" id={`item-${item.id}`} checked={item.completed} onChange={() => handleChange(item.id)} /> <label htmlFor={`item-${item.id}`}>{item.text}</label> </div> ); } class App extends React.Component { constructor() { super(); this.state = { todos: TodosData, }; this.handleChange = this.handleChange.bind(this); } handleChange(id) { this.setState((prevState) => { const updatedTodo = prevState.todos.map((todo) => { return todo.id === id ? { ...todo, completed: !todo.completed, } : todo; }); return { todos: updatedTodo, }; }); } render() { const { todos } = this.state; return ( <div className="todo-list"> {todos.map((item) => ( <TodoItem key={item.id} item={item} handleChange={this.handleChange} /> ))} </div> ); } } ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') );
 body { font-family: sans-serif; line-height: 1.6; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id="root"></div>

Instead of using an additional variable, you can do it in one line. Can u see if this works for you.

handleChange(id) {
    this.setState(prevState => prevState.map(todo => todo.id === id ? {...todo, completed: !todo.completed} : todo )
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM