Skip to content

Instantly share code, notes, and snippets.

@andrewsantarin
Last active November 26, 2019 06:23
Show Gist options
  • Save andrewsantarin/6f324c3dd6ff8f70e83d22523e21327e to your computer and use it in GitHub Desktop.
Save andrewsantarin/6f324c3dd6ff8f70e83d22523e21327e to your computer and use it in GitHub Desktop.

react-auto-controlled explained

Some components

Suppose you have a component that uses an autocontrolled manager. Let's call it <Number />.

class Number extends React.Component {
  state = {
    // Initialize the component class instance with
    // the prop you provided from the outside.
    number: this.props.number || 0,  // defaults to 0
  };

  // ...rest of the "AutoControlledManager()" implementation
  trySetState = NumberAutoControlledManager.trySetState;

  // increment state.number then pass that new value to the component's parent callback function.
  incrementNumber = () => {
    const nextNumber = this.state.number + 1;
    this.props.onIncrementClick(nextNumber);
    this.setState({ // <-- Updates the number state; Ignores the number prop given by the parent.
      number: nextNumber,
    });
  }

  render() {
    console.log(this.state.number);
    return (
      <div>
        <button onClick={this.incrementNumber} /> Value: {this.state.number}
      </div>
    );
  }
}

Suppose you have another component on top which contains that component. Let's call it <App />.

// Number value starts at 0 since we passed nothing.
// A click of the button will add 1 to the number even though we didn't pass a state to it.
const App = <div><Number /></div>;

Suppose you want this <App /> component to control the value displayed. The value starts with 123, not 0, and you want to decrement instead of increment.

const App = () => {
  const [ value, setValue ] = React.useState(123);
  const decrementValue = () => setValue(value => value - 1);
  return <div><Number number={value} onIncrementClick={decrementValue} /></div>;
};

setState vs. trySetState

See this line?

  incrementNumber = () => {
    // ...other implementation
    this.setState({ // <-- Updates the number state; Ignores the number prop given by the parent.
      number: nextNumber,
    });
  }

With setState, your number update history will be like this:

123
124

But the parent component asked to decrease the number with its own function, right?

  const decrementValue = () => setValue(value => value - 1);

Enter trySetState:

  incrementNumber = () => {
    // ...other implementation
    this.trySetState({ // <-- Updates the number state only if the number prop wasn't given by the parent.
      number: nextNumber,
    });
  }
123
122

The number would be decreased as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment