user3454581
user3454581

Reputation: 572

React state changed not re rendering child

I Would like to bind two child field together. I have created change handler in parent class and passing state value in props of child. whenever i type something in textfield, Parent state and child props are updated i can see that in react dev tool, but child state is not reflected.

Below is my code

class Parent extends React.Component{

    constructor(props){
        super(props);
        this.state = {value: "test"};
        this.changeHandler = this.changeHandler.bind(this);
    }

    changeHandler(value){
        this.setState({value: value});
    }

    render(){
        return (
                <div>
                    <Child value={this.state.value} change={this.changeHandler}/>
                    <Child value={this.state.value} change={this.changeHandler}/>
                </div>
                );
    }
}

class Child extends React.Component{

    constructor(props){
        super(props);
        this.changeHandler = this.changeHandler.bind(this);
        this.state = {value: this.props.value};
    }

    changeHandler(e){
        this.setState({value:e.target.value});
        this.props.change(e.target.value);
    }

    render(){
        return (
                <input type="text" value={this.state.value} onChange={this.changeHandler} />
                );
    }

}

ReactDOM.render(<Parent/>, document.getElementById('root'));

Upvotes: 0

Views: 110

Answers (3)

dance2die
dance2die

Reputation: 36985

It's because you are using

class Child extends React.Component{

    constructor(props){
        super(props);
        this.changeHandler = this.changeHandler.bind(this);
        // ...............  👇
        this.state = {value: this.props.value};
    }
    // ... 
}

Your internal this.state.value is set only "once" on component mount phase.
(That means, whenever Parent -> this.state.value changes and causes the Child components to re-render, this.state = {value: this.props.value}; won't be invoked again)

Instead of using the state in Child component, use the this.props.value directly on your <Input /> field.

<input type="text" value={this.props.value} onChange={this.changeHandler} />

So the flow is, someone types something in Child.input, which tells Parent to update the state, and the change is trickled back down to your Child -> this.props.value.

And then you notify Parent by calling the passed this.props.changeHandler.

So the final, Child component would look like following.

class Child extends React.Component{
    constructor(props){
        super(props);
        this.changeHandler = this.changeHandler.bind(this);
    }

    changeHandler(e){
        this.props.changeHandler({value:e.target.value});
    }

    render(){
        return (
          <input type="text" value={this.props.value} onChange={this.changeHandler} />
        );
    }

}

If you use the ES6 "arrow functions" syntax, you can make it smaller, removing the need for this.changeHandler = this.changeHandler.bind(this).
(It's because an arrow function doesn't create its own this but uses calling context's this)

class Child extends React.Component{
    constructor(props){
        super(props);
    }

    changeHandler = (e) => {
        this.props.changeHandler({value:e.target.value});
    }

    render(){
        return (
          <input type="text" value={this.props.value} onChange={this.changeHandler} />
        );
    }

}

Or you can call it directly from onChange.

class Child extends React.Component {
  render() {
    return (
      <input
        type="text"
        value={this.props.value}
        onChange={this.props.changeHandler}
      />
    );
  }
}

We can go one step further by converting it to a Function Component (NOT "Functional" component because it doesn't have anything to do with Functional Programming)

function Child({ value, changeHandler}) {
  return <input type="text" value={value} onChange={changeHandler} />
}

// or
const Child = ({value, changeHandler}) => 
  <input type="text" value={value} onChange={changeHandler} />

And when you use const Child = ..., make sure that it's declared BEFORE Parent. But when you use function Child, it can show up before or after Parent due to how JavaScript hoisting works.

Lastly, no change in Parent is needed.

Upvotes: 5

Zeel Shah
Zeel Shah

Reputation: 462

You need to implement componentDidUpdate

class Parent extends React.Component{

constructor(props){
    super(props);
    this.state = {value: "test"};
    this.changeHandler = this.changeHandler.bind(this);
}

changeHandler(value){
    this.setState({value: value});
}

render() {
    return (
            <div>
                <Child value={this.state.value} change={this.changeHandler}/>
                <Child value={this.state.value} change={this.changeHandler}/>
            </div>
            );
    }
 }

class Child extends React.Component{

constructor(props){
    super(props);
    this.changeHandler = this.changeHandler.bind(this);
    this.state = {value: this.props.value};
}


changeHandler(e){
    this.setState({value:e.target.value});
    this.props.change(e.target.value);
}

componentDidUpdate(prevProps) {
  if(prevProps.value !== this.props.value) {
      this.setState({value: this.props.value});
  }
}

render(){
    return (
            <input type="text" value={this.state.value} onChange={this.changeHandler} />
            );
}

}
ReactDOM.render(<Parent/>, document.getElementById('root'));

Upvotes: 2

azundo
azundo

Reputation: 6052

Each child component has its own independent state. If you want the value to be shared between the two you remove it from state in the children and just use this.props.value in the children. Then value is only mutable in the parent.

Upvotes: 0

Related Questions