React Components and Global State

There’s a ton of good React documentation out there. If you have a question about the framework, chances are somebody else has had that question before and they’ve either sought answers on a forum or they found the answer easily in the available docs online. It doesn’t hurt that the official React docs strike an awesome balance of being descriptive and concise (when compared to other official docs out there) with plenty of widely applicable examples.

One seemingly elusive topic is when and why we use certain types of components. Should we make a form a PureComponent? Should we default to functional components? What’s a component again? There are blog posts abounding on the subject and they all say different things. So I’m here to contribute to the discourse with fresh o̸p̸i̸n̸i̸o̸n̸s̸ facts.

I realize that React was designed with modularity in mind and as a consequence there isn’t really a hard and fast doctrine of when and where to use different types of components. Those decisions are left to the designer/developer and chances are there are a number of approaches that you can get away with. So with that I’ll offer up my thoughts on the matter.

Let’s start with the simplest component type: the functional component. These are just normal javascript functions that accept a props object as an argument and return JSX (a single element to be exact).

function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}

These components are light and fast compared to their brethren. Historically functional components are stateless, and they don’t have access to React’s component lifecycle methods. However, React 16.8 introduced hooks which allow functional components to use local state, and allow for implementation of custom lifecycle methods. It is possible (and totally acceptable) to connect functional components to global state too (if you’re using a global state manager like Redux).

A good rule of thumb for building React components is to start off defining a component functionally, and if you need to use multiple lifecycle methods, switch it to a class component.

Ok now let’s talk about the component type that will pretty much always work: the class component. These things are the JSX equivalent of those massive Swiss Army knives with an absurd amount of tools. Will they be able to get the job done? Yes. Are they light and wieldy? Alas, not really.

go go gadget ice cream scoop!

Full blown class components have access to all of React’s component lifecycle methods. If you want to fetch some data once your component is rendered to the DOM or do some special action every time the component updates then the full class component is probably the answer. Class components are just ES6 classes that extend React’s built in Component class and any lifecycle method can be called directly in the class definition:

class Welcome extends React.Component {
componentDidMount() {
fetch("http://somedomain.com/api/resources")
.then(response => response.json())
.then(result => result.doSomething())
}

Yes, the behavior defined above can be implemented using a functional component and the useEffect()hook. It can also be implemented with React.Component’s svelte cousin — React.PureComponent. Pure components automatically run the normal component shouldComponentUpdate()lifecycle method and check to see if the component needs to re-render whenever the state changes. There is a trick though: Pure components only do a shallow comparison of state, so if you have properties in your state that are deeper than one level, there’s a chance your pure component won’t re-render.

The takeaway? Start building your components functionally, if you need multiple lifecycle methods then upgrade to a pure component, if you need special behavior in the shouldComponentUpdate()lifecycle method then use a normal class component. Oh ya and don’t give your state deeply nested attributes.

Cheers