1

I was reading this React related article. I have also looked at the discussion on closures and callbacks here and here and here.

In an example, given in the React related article, there are three components <App>, <Form>, <Button>. <App> passes a state value to <Form> component during initialization and also during componentDidMount lifecycle. <Form> passes a callback function to <Button> component along with the state value it received from <App>.

class App extends React.Component {
  state = { val: "one" }

  componentDidMount() {
    this.setState({ val: "two" })
  }

  render() {
    return <Form value={this.state.val} />
  }
}

const Form = props => (
  <Button
    onClick={() => {
      alert(props.value)
    }}
  />
)

When the button is clicked in the below <Button> component, it alerts a stale value "one" instead of the latest value "two".

class Button extends React.Component {
.....
  render() {
    return (
        <button onClick={this.props.onClick}>This one is stale</button>
    )
  }
}

The reason given in the React article for stale value is that the callback closes over props.value.

I do not understand how/why is the props.value being closed over in React component. The callback assignment in above is probably being done like this:

this.props.onClick = () => {
      alert(props.value)
    }

So why is the latest value not being picked up? How is the prop.value "closed over"?

The complete code is given here.

1 Answer 1

1

This is the complete code for the Button component:

class Button extends React.Component {
  shouldComponentUpdate() {
    return false;
  }

  render() {
    return (
      <div>
        <button onClick={this.props.onClick}>This one is stale</button>
        <button onClick={() => this.props.onClick()}>This one works</button>
      </div>
    );
  }
}

The Button component is set up to not rerender when passed new props, thus this.props.onClick will only be evaluated once on initial render and will always reference the first onClick function that was passed to it.

In contrast, even though it is also evaluated once, () => this.props.onClick() is set up to look up onClick on the component current props at the time the arrow function is actually executed. At that point, the props will have been updated with the new version of onClick.

Sign up to request clarification or add additional context in comments.

5 Comments

Are you saying that in the first case the props.value in alert(props.value) will be substituted with "one" - like this.props.onClick = () => alert("one")
No. Form will render twice, each time with a different set of props and passing Button a different onClick function. Each onClick function closes over - ie keeps a reference to - the props passed to Form at the time of render - so props.value will always evaluate to "one" for the first onClick function, and "two" for the second.
But ultimately, the reason why you're seeing the result that you see is because Button is set up to never rerender.
What exactly does "closes over" mean, is it substitution of values ?
It just means that a function captures and keeps a live reference to something - more specifically a variable that is in scope at declaration time. This is important because in javascript, functions can be executed in a different scope than the one in which they are declared.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.