zyklus zyklus - 10 days ago 7
Javascript Question

Is there a state mutator observer in react?

tl;dr -- I want to fire a callback whenever the state changes in a component




I have a component that mutates
this.props.items
and fires
this.props.onChange
on every mutation. I'm currently doing something like this:

removeItem(ix) {
const items = this.state.items.slice();
items.splice(ix, 1);
this.setState({ items });
this.triggerOnChange();
}


I'm wondering if it's possible to remove the manual call to
triggerOnChange
and instead do it whenever
this.state.items
is updated

Answer

You could override setState, if you want to wrap the state change:

setState(state) {
  console.log("New state - before: ", state);
  const retVal = super.setState(state);
  console.log("New state - after");
  return retVal;
}

(That may be overkill, I don't think setState is documented to have a return value; but frustratingly, as far as I can tell, it's not documented not to...)

Example:

class MyThingy extends React.Component {
  constructor(props) {
    super(props);
    this.state = {...props};
  }
  
  render() {
    return (
      <div onClick={() => this.update()}>
      <div>{this.state.title}</div>
      {this.state.items.map(e => (
            <div>{e}</div>
      ))}
      </div>
    );
  }
  
  update() {
    this.setState({
      items: ['a', 'b', 'c'],
      title: "After"
    });
  }
  
  setState(state) {
    console.log("New state - before: ", state);
    const retVal = super.setState(state);
    console.log("New state - after");
    return retVal;
  }
}

ReactDOM.render(
  <MyThingy items={[]} title="Before - click me" />,
  document.getElementById("react")
);
<div id="react"></div>
<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>

Alternately, you might turn this on its head and look at MobX.