State Management in React Components
In this article, I am going to deal with State Management in React components. It is quite a confusing and an important aspect of React application development. Let’s start with basics of state and how to manage it.
What is state?
If you are familiar with Web development, then you are well aware of the word “State”. A state is defined as a configuration, attributes, conditions or information content of an application like component’s current selected tab, user details or a timer status. Virtually, all components in an application have state. A component may have more than one possible state and its current state is one of all possible states and it changes from one state to another, over time, when triggered by some kind of event. The event could be a network message, a timer expiring or a tab selection.
State in React application
A state can be divided mainly into two types, depending on their nature
- react state (UI data): It is component’s own data, maintained within the component like “current tab selected” which is generally non-critical and ephemeral data.
- store (Application central data): It is core application data implemented using redux, flux or mobex like “Back-end data fetched via network requests”.
When I started coding in React, I was totally confused whether to keep the data in react state or central store. I used to maintain all data related to react’s components in local state. However, when I wanted to share the local state of one component to another component, I used to move this data to central store. That was when I came up with a thumb rule to help in deciding what data to maintain in react state.
State of a component should be independent of props passed.
For example, we have a component which displays currency formatted number.
import React from 'react';
import currencyFormatter from 'util/formatter';
class NumberFormatter extends React.Component {constructor(props) {
this.state = {
formattedNumber: currencyFormatter(props.number)
};
}
componentWillReceiveProps(nextProps) {
this.setState({
formattedNumber: currencyFormatter(nextProps.number)
});
}render() {
return (
<div>
<h2>{this.state.formattedNumber}</h2>
</div>
);
}}
export default NumberFormatter;
A number is passed via props and not component’s own data. What’s wrong with this? Whenever props update, we need to re-format the number once again using React’s componentWillReceiveProps hook. Moreover, we are adding an unnecessary complexity of setState and diluting the react state with unnecessary data. Now, take a look at the below code.
import React from 'react';
import currencyFormatter from 'util/formatter';
class NumberFormatter extends React.Component {getFormattedNumber = () => currencyFormatter(this.props.number)render() {
const formattedNumber = getFormattedNumber();
return (
<div>
<h2>{formattedNumber}</h2>
</div>
);
}}
export default NumberFormatter;
The number passed via props is formatted in the render. Thus, the complexity of component’s state is reduced and render has the full power in decision making. Indeed this makes local state in component as minimal as possible. It also enhances the server-side rendering of application.
However, performing all time-consuming operations in render will kill the performance of the application. We can use memoization function of Lodash to avoid repeated executing of the complex functions.
Conclusion:
While designing a component, always think of the minimal set of mutable state that your component needs and compute everything else you need on-demand. Maintain only atomic values in the state of a component and not any derived values or duplicate values.
