0
votes

This is the code from the parent:

_handleSelectedTodo(e) {
  e.preventDefault(); 
  this.setState({checkedIds: [...this.state.checkedIds, e.target.value]});
}
// render () { ...
<ul>
{todos.map((todo,todoIndex) => // The map method has default argument on second index, the index of the current todo in map execution. We use it so we do not have to use findIndex when we need to dispatch action that needs index of a specific todo
  <Todo
    key={todo.id}
    {...todo} // pass all the todo property
    onClick={() => onTodoClick(todo.id)}
    onTrashClick={() => onDeleteClick(todoIndex)}
    handleSelectedTodo = {this._handleSelectedTodo}
    checked={Boolean(this.state.checkedIds.includes(todo.id))}
  />
)}

This is what I tried since assigning props on checkbox doesn't update on the Child Todo code, checked can be true or false that will depend on props:

componentWillMount() {
  this.setState({ checked: this.props.checked })
}
// render() { ...
<input
  checked={this.state.checked}
  onChange={handleSelectedTodo}
  type="checkbox"
  value={id}
></input>

Checking on the checkbox updates the parent component state but the checkbox on child is not checked. help?

2

2 Answers

1
votes

This code can also be written this way:-

_handleSelectedTodo(e) {
  e.preventDefault(); 
  this.setState({checkedIds: [...this.state.checkedIds, e.target.value]});
}
render () { ...
<ul>
{todos.map((todo,todoIndex) => {
  let isChecked = (this.state.checkedIds.indexOf(todo.id) >= 0);
  return (
     <Todo
       key={todo.id}
       {...todo} // pass all the todo property
       onClick={(id) => onTodoClick(todo.id)} //This event should be binded to 'this' inside the constructor
       onTrashClick={(todoIndex) => onDeleteClick(todoIndex)}
       handleSelectedTodo = {this._handleSelectedTodo}
       checked={isChecked}
     />
  )
 });
}

This child component should depend on it's parent state for it's checked property

<input
  checked={this.props.checked}
  onChange={handleSelectedTodo}
  type="checkbox"
  value={id}
></input>

If you want to keep the checked property of child as a state of child as well, then what Mayank said about componentWillReceiveProps would be the right approach.

1
votes

Reason is, componentWillMount will get called only before the initial rendering not after that, when you are updating the state of parent component, state of child component is not getting updated.

Use componentWillReceiveProps method to update the state of child component:

componentWillReceiveProps(nextProps){
   if(nextProps.checked != this.state.checked)
        this.setState({checked: nextProps.checked})
}

Or simplest solution will be don't save the value in state of child component, directly use the props values, Like this:

<input
    checked={this.props.checked}      //here use this.props.checked directly
    onChange={handleSelectedTodo}
    type="checkbox"
    value={id}
>
</input>

componentWillReceiveProps:

componentWillReceiveProps() is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.

componentWillMount:

componentWillMount() is invoked immediately before mounting occurs. It is called before render().