One does not simply “setState”

The situation

A while ago when developing an SPA for a client, I encountered (again a simple looking) requirement where I had to update several counter on receiving some events from the server over WebSockets and another that directly corresponded to the number of clicks made on a button. Think of it like a dynamically updating counter on a dashboard.

A little recap…

A React component is like a function of externally supplied props and some internal state which returns HTML that is rendered in the browser. State and props affect how the component renders and behaves. The state is local to the component and can only be initialized and updated within the component.

class Counter extends React.Component {
state = {
event: 0,
clicks: 0
}
componentDidMount() {
monitoring(...).subscribe(() => this.setState({
event: this.state.value + 1
}));
}
handleButtonClick = e => this.setState({
clicks: this.state.clicks + 1
})
render() {
// ...
}
}

Why?

Because setState is asynchronous. React can batch state updates and defer rendering till it seems fit for performance reasons. So when setState is applied, the value of the current state is always stale, which resulted in missing click and event counts.

Introducing the setState with callbacks

setState accepts a callback which computes state as a function of the current state, like this:

this.setState((currentState) => ({
click: currentState.click + 1
});

Reusable setState

There is another interesting side-effect of this pattern. setState is now a function that can be shared and composed between different components which leads to better code re-usability.

Product Engineer @ Gojek. Open-source contributor. Find me at https://amitosh.in/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store