Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactJS clearing an input from parent component

I'm teaching myself react with a super simple app that asks the user to type a word presented in the UI. If user enters it correctly, the app shows another word, and so on.

I've got it almost working, except for one thing: after a word is entered correctly, I need to clear the input element. I've seen several answers here about how an input element can clear itself, but I need to clear it from the component that contains it, because that's where the input is checked...

// the app
class AppComponent extends React.Component {
    constructor() {
      super();
      this.state = {
        words: ['alpha', 'bravo', 'charlie'],
        index: 0
      };
    }
    renderWordsource() {
      const word = this.state.words[this.state.index];
      return <WordsourceComponent value={ word } />;
    }
    renderWordinput() {
      return <WordinputComponent id={1} onChange={ this.onChange.bind(this) }/>;
    }
    onChange(id, value) {
        const word = this.state.words[this.state.index];
        if (word == value) {
            alert('yes');
            var nextIndex = (this.state.index == this.state.words.count-1)? 0 : this.state.index+1;
            this.setState({ words:this.state.words, index:nextIndex });
        }
    }
    render() {
      return (
        <div className="index">
          <div>{this.renderWordsource()}</div>
          <div>{this.renderWordinput()}</div>
        </div>
      );
    }
}

// the input component
class WordinputComponent extends React.Component {
    constructor(props) {
        this.state = { text:''}
    }
    handleChange(event) {
      var text = event.target.value;
      this.props.onChange(this.props.id, text);
    }
    render() {
      return (
        <div className="wordinput-component">
          <input type="text" onChange={this.handleChange.bind(this)} />
        </div>
      );
    }
}

See where it says alert('yes')? That's where I think I should clear the value, but that doesn't make any sense because it's a parameter, not really the state of the component. Should I have the component pass itself to the change function? Maybe then I could alter it's state, but that sounds like a bad idea design-wise.

like image 846
user1272965 Avatar asked Apr 06 '17 22:04

user1272965


People also ask

How do I pass data back from parent to child in React?

To pass data from child to parent component in React:Pass a function as a prop to the Child component. Call the function in the Child component and pass the data as arguments. Access the data in the function in the Parent .

How do you clear the state of a child component?

In the child function component, you add a clearState function. You pass the clearState function from the child component to the parent by using one of the props function. In the resetForm of parent component, call the clearState function to clear child state.


2 Answers

The 2 common ways of doing this is controlling the value through state in the parent or using a ref to clear the value. Added examples of both

The first one is using a ref and putting a function in the child component to clear The second one is using state of the parent component and a controlled input field to clear it

class ParentComponent1 extends React.Component {
  state = {
    input2Value: ''
  }
  clearInput1() {
    this.input1.clear();
  }
  clearInput2() {
    this.setState({
      input2Value: ''
    });
  }
  handleInput2Change(evt) {
    this.setState({
      input2Value: evt.target.value
    });
  }
  render() {
    return (
      <div>
        <ChildComponent1 ref={input1 => this.input1 = input1}/>
        <button onClick={this.clearInput1.bind(this)}>Clear</button>
        <ChildComponent2 value={this.state.input2Value} onChange={this.handleInput2Change.bind(this)}/>
        <button onClick={this.clearInput2.bind(this)}>Clear</button>
      </div>
    );
  }
}

class ChildComponent1 extends React.Component {
  clear() {
    this.input.value = '';
  }
  render() {
    return (
      <input ref={input => this.input = input} />
    );
  }
}

class ChildComponent2 extends React.Component {
  render() {
    return (
      <input value={this.props.value} onChange={this.props.onChange} />
    );
  }
}

ReactDOM.render(<ParentComponent1 />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
like image 169
noveyak Avatar answered Sep 24 '22 06:09

noveyak


I had a similar issue: I wanted to clear a form which contained multiple fields.

While the two solutions by @noveyak are working fine, I want to share a different idea, which gives me the ability to partition the responsibility between parent and child: parent knows when to clear the form, and the items know how to react to that, without using refs.

The idea is to use a revision counter which gets incremented each time Clear is pressed and to react to changes of this counter in children.

In the example below there are three quite simple children reacting to the Clear button.

class ParentComponent extends React.Component {
  state = {revision: 0}
  clearInput = () => {
    this.setState((prev) => ({revision: prev.revision+1}))
  }
  render() {
    return (
      <div>
        <ChildComponent revision={this.state.revision}/>
        <ChildComponent revision={this.state.revision}/>
        <ChildComponent revision={this.state.revision}/>
        <button onClick={this.clearInput.bind(this)}>Clear</button>        
      </div>
    );
  }
}
class ChildComponent extends React.Component {
  state = {value: ''}
  componentWillReceiveProps(nextProps){
    if(this.props.revision != nextProps.revision){
      this.setState({value : ''});
    }
  }
  saveValue = (event) => {
    this.setState({value: event.target.value})
  }
  render() {
    return (
      <input value={this.state.value} onChange={this.saveValue} />
    );
  }
}
ReactDOM.render(<ParentComponent />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

EDIT: I've just stumbled upon this beautifully simple solution with key which is somewhat similar in spirit (you can pass parents's revision as child's key)

like image 23
qbolec Avatar answered Sep 22 '22 06:09

qbolec