Matthew
Matthew

Reputation: 11270

How to reset child elements' state?

Let's say I have three elements that hold in state a counter that increments when clicked.

If I click on one element, how do I reset the other counters to 0?

https://jsfiddle.net/69z2wepo/56827

const Parent = React.createClass({
    render() {
        const rows = [];
        for (let i = 0; i < 3; i++) {
            rows.push(<Child key={i} />);
        }
        return (
            <ul>
                {rows}
            </ul>
        );
    }
});

const Child = React.createClass({
    getInitialState() {
        return {counter: 0};
    },
    handleClick() {
        this.setState({counter: ++this.state.counter });
    },
    render() {
        return (
            <div onClick={this.handleClick}>
                {this.state.counter}
            </div>
        );
    }
});

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

Upvotes: 15

Views: 18285

Answers (4)

Sandip Jadhav
Sandip Jadhav

Reputation: 173

const Parent = () => {
   let clearFunc = null; // child clear state function will be stored in this variable
   // in parent component, call this clearChild function.
   const clearChild = () => {
     typeof childClearFunc === 'function' && childClearFunc();
   }
   return <Child handleClear={(func) => clearFunc = func } />
}

const Child = () => {
  const handleClear = () => {
    // code to clear everything in child
  };

  if (props.handleClear) {
    props.handleClear(handleClear);
  }
}

Upvotes: 0

tlrobinson
tlrobinson

Reputation: 2858

It you really can't lift the state into the parent component as other answers have suggested, a hacky way to do this is to just change the key prop of an element. This causes React to unmount the element with the old key and mount a new instance.

class ChildComponent extends React.Component {
  state = {
    count: 0
  };
  render() {
    const { count } = this.state;
    return (
      <div>
        <div>count: {count}</div>
        <button onClick={() => this.setState({ count: count + 1 })}>Increment</button>
      </div>
    );
  }
}

class ParentComponent extends React.Component {
  state = {
    stateBustingKey: 0
  };
  render() {
    const { stateBustingKey } = this.state;
    return (
      <div>
        <ChildComponent key={stateBustingKey} />
        <button
          onClick={() =>
            this.setState({ stateBustingKey: stateBustingKey + 1 })
          }
        >
          Reset
        </button>
      </div>
    );
  }
}

ReactDOM.render(<ParentComponent />, document.getElementById("root"));
<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>
<div id="root" />

Upvotes: 33

Piotr Białek
Piotr Białek

Reputation: 2709

Component parent in this case should be managed the state of children.

Check this:

const Parent = React.createClass({
    getInitialState() {
        return {counter: 0, id: 0};
    },
    handleClick(id) {
        if(this.state.id == id){
            this.setState({counter: ++this.state.counter, id: id });
        } else {
            this.setState({counter: 1, id: id });   
        }

    },
    getCounter(id){
        if(id == this.state.id){
            return this.state.counter;
        } else {
            return 0;
        }
    },
    render() {
        const rows = [];
        for (let i = 0; i < 3; i++) {
            rows.push(<Child key={i} counter={this.getCounter(i)} handleClick={this.handleClick} id={i} />);
        }
        return (
            <ul>
                {rows}
            </ul>
        );
    }
});

const Child = React.createClass({

    render() {
        return (
            <div onClick={this.props.handleClick.bind(null, this.props.id)}>
                {this.props.counter}
            </div>
        );
    }
});

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

JsFiddle

Upvotes: 3

QoP
QoP

Reputation: 28397

That would be a little hard since your Child components are managing their own state.

You can convert them into dumb components and manage their state in your Parent component.

Something like this

const Parent = React.createClass({
    getInitialState() {
        return {counters: [0,0,0]};
    },
    handleClick(index){
       let newCounterState = this.state.counters.map(() => 0);
       newCounterState[index] = this.state.counters[index] + 1 ;
       this.setState({counters : newCounterState})
    },
    render() {
        const rows = this.state.counters.map((value,index) => (
            <Child 
               key={index}
               handleClick={() => this.handleClick(index)}
               counter={value}
            />
        ))
        return (
            <ul>
                {rows}
            </ul>
        );
    }
});

const Child = ({counter,handleClick}) => (
    <div onClick={handleClick}>
      {counter}
  </div>
)

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

jsfiddle

Upvotes: 5

Related Questions