Edgar Navasardyan
Edgar Navasardyan

Reputation: 4501

Making the child's input dependent on parent input value

My goal is to

  1. initialize the child's state with the parent's respective value. To do this, I set the child's state value in the constructor using the following code:
  2. Reset the value of child as soon as the parent'
  3. Retain the ability to change the child's value

To achieve the second goal, I use getDerivedStateFromProps the following way https://codepen.io/jedgar-nawasardqn/pen/VRpWrZ?editors=1011

class Parent extends React.Component { 
    constructor(props) {
        super(props)
        this.state = {
          value: "default valu"
        }
    }
    onChange = (value) => {
         this.setState({
              value: value
         })
    }

    render() {

      return (
          <div>
              <form>
                 <label>Parent: </label>
                 <input
                 value={this.state.value}
                 onChange={(e) => this.onChange(e.target.value)}/>
              </form>
              <Child
                 value={this.state.value}/>
          </div>);
      }
}

class Child extends React.Component {

  constructor(props) {
        super(props)
        this.state = {
          value: this.props.value
        }
    }
   static getDerivedStateFromProps(nextProps, prevState) {
     console.log('nextProps', nextProps)   
     return {
            value: nextProps.value
        }
    }
  onChange = (value) => {
         console.log('CHild changed')
         this.setState({
              value: value
         })
    } 
  render() {
       return (
         <div>
           <label>Child: </label>
           <input
                value={this.state.value}
                onChange={(e) => this.onChange(e.target.value)}/>
         </div>)
   }
}

React.render(<Parent/>, document.getElementById('app'));

...but for some reason I can't make it work. Any suggestions ?

Upvotes: 0

Views: 120

Answers (2)

Tom Finney
Tom Finney

Reputation: 2918

Assuming I understood your goals of wanting to have a parent input control a child input when the parent changes, but allowing the child input to change itself and not affect the parent:

The problem is that getDerivedStateFromProps gets called regardless so when you are changing the state from the Child's input, getDerivedStateFromProps is immediately reverting it back to the Parent's value. You should use componentDidUpdate and diff the props rather than using getDerivedStateFromProps and only update the Child's state to match the Parent's when the Parent's state actually changes.

I made a Code Sandbox based on your Fiddle to demonstrate this: https://codesandbox.io/s/m5qj6qq769?fontsize=14

Upvotes: 1

Jolly
Jolly

Reputation: 1768

First, even if it's not about your question, I suggest you to not use an arrow function inside the render() method, so, instead of writing onChange={(e) => this.onChange(e.target.value)} />, you should just write onChange={this.onChange} />. The reason is that the arrow function would be created and then discarded at every render(): not a big deal if we have few renders and few arrow functions, but.. It's better to avoid waste of computational power :)

Now, about your question! Honestly, it seems that the Child Component just need to show the value, thus, it's not really useful to "copy" the value from Parent Component to Child Component: you can just keep your values in the Parent Component, and then refers to that value in both Parent and Child Component. To do this, you need to pass an handler function also to the Child Component. Let's look at the fiddle!

class Child extends React.Component {
    constructor(props) {
        super(props);
    }
    
    render() {
        return (
            <div>
                <label>Child: </label>
                <input
                    value={this.props.value}
                    onChange={this.props.handleChange}/>
            </div>
        );
    }
}

class Parent extends React.Component {
    constructor(props) {
        super(props);
        
        this.state = {value: "Default Value"};
    }
    
    handleChange = (e) => {
        this.setState({value: e.target.value});
    }
    
    render() {
        return (
            <div>
                <form>
                    <label>Parent: </label>
                        <input
                            value={this.state.value}
                            onChange={this.handleChange} />
                </form>
                <Child
                    value={this.state.value}
                    handleChange={this.handleChange} />
          </div>
        );
    }
}

ReactDOM.render(<Parent />, document.getElementById('root'));
@import url(https://fonts.googleapis.com/css?family=Montserrat);

body {
    font-family: 'Montserrat', sans-serif;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

<div id='root'></div>

Upvotes: 0

Related Questions