Gaurav Soni Gaurav Soni - 1 month ago 16
React JSX Question

Checkbox toggle in react

Here in my app the function 'todoCompleted' is invoked when list item or checkbox is clicked. When i click the checkbox the code is not running correctly.The function is used for both li element and checkbox field.



class App extends React.Component {



constructor(){
super();
this.state={
todo:[]
};
};

entertodo(keypress){
var Todo=this.refs.inputodo.value;
if( keypress.charCode == 13 )

{
this.setState({
todo: this.state.todo.concat({Value:Todo, checked:false})

});
this.refs.inputodo.value=null;
};
};
todo(todo,i){
return (
<li className={todo.checked===true? 'line':'newtodo'}>
<div onClick={this.todoCompleted.bind(this,i)}>
<input type="checkbox" className="option-input checkbox" checked={todo.checked} />
<div key={todo.id} className="item">
{todo.Value}
<div className="Button">
<span className="destroy" onClick={this.remove.bind(this, i)}>X</span>
</div>
</div>
</div>
</li>
);
};

remove(i){
this.state.todo.splice(i,1)
this.setState({todo:this.state.todo})
};
todoCompleted(i){
var todo=this.state.todo;
{
todo[i].checked =true;
this.setState({
todo:this.state.todo
});
}
};
allCompleted=()=>{
var todo = this.state.todo;
var _this = this
todo.forEach(function(item) {
item.className = _this.state.finished ? "newtodo" : "line"
item.checked = !_this.state.finished
})
this.setState({todo: todo, finished: !this.state.finished})
};

render() {
return (
<div>
<h1 id='heading'>todos</h1>
<div className="lines"></div>
<div>
<input type="text" ref= "inputodo" onKeyPress={this.entertodo.bind(this)}className="inputodo"placeholder='todos'/>
<span onClick={this.allCompleted}id="all">x</span>
</div>
<div className="mainapp">
<ul>
{this.state.todo.map(this.todo.bind(this))}
</ul>
</div>
</div>
);
}
}


ReactDOM.render(<App/>,document.getElementById('app'));

.line {
text-decoration: line-through;
color: red;
}
.newtodo{
text-decoration: none;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="app"></div>




Answer

The todoCompleted method is invoked 2 times when you click on the input element. It is also bound 3 times for each li element: on li, on input, and on button click. As a quick fix, you could reformat the structure of your li element in the following way:

Demo here: http://codepen.io/PiotrBerebecki/pen/bwmwqz

      <li className={text.Decor}>
        <div onClick={this.todoCompleted.bind(this,i)}>
          <input type="checkbox"
                 className="option-input checkbox"
                 checked={text.checked} />
          <div key={text.id}
               className="item">
            {text.Value}
          </div>
        </div>
        <button type="button"
                className="destroy"
                onClick={this.remove.bind(this,i)}>
          X
        </button>
      </li>

Please note

  • the new div structure,
  • i is now passed to the remove method, and also that
  • the todoCompleted method is bound only once. For performance reasons, you should avoid using bind or arrow functions when rendering JSX. This is because a copy of the event handling function is created for every instance generated by the map() function. This is explained here: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md

Also you have been mutating the state:

  remove(i){
    this.state.todo.splice(i,1)
    this.setState({todo:this.state.todo})
  };

Instaed of the above you can:

  remove(i){
    this.setState({
      todo: [
        ...this.state.todo.slice(0, i),
        ...this.state.todo.slice(i + 1)
      ]
    });
  };
Comments