How to keep React components small

Alfred Yang
finnovate.io
Published in
3 min readJun 12, 2022
Photo by Volodymyr Hryshchenko on Unsplash

As a rule of thumb for our team at Finnovate.io, if a React component has more than 200 lines of code, then it is too big. Big components are difficult to read, difficult to maintain and nearly impossible to unit test. Fortunately, it is not so hard to abstract code away from components with a few pointers.

Import pure functions

When refactoring a large component, one of the first things I would look for are business logic and functions that are not directly tied to the component state. For example, if you have a helper function in your component that looks like this:

function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}

There is no reason to keep that function in the component itself. We can simply move this function into a separate file:

export function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}

And import the function into the component:

import { capitalizeFirstLetter } from './stringlib';

Now the function and the business logic within it can be unit tested independently and reused by multiple components. If possible, keep functions pure they are deterministic and very easy to unit test.

Use custom React hooks

React hooks allow developers to do something that wasn’t possible before its introduction — they allow for the abstraction of code that is coupled with component state. For example, if the code in your component looks like this:

const [ customers, setCustomers ] = useState([]);
const [ isLoading, setIsLoading ] = useState(false);
const [ errMessage, setErrMessage ] = useState('');
useEffect(() => {
setIsLoading(true);
// Calls API to get list of customers
getCustomers().then((response) => {
setCustomers(response.data.customers);
setIsLoading(false);
setErrMessage('');
}).catch((e) => {
setErrMessage(e.message);
setIsLoading(false);
});
}, []);

Much of this code can be moved into a custom hook:

export function useCustomers() {
const [ customers, setCustomers ] = useState([]);
const [ isLoading, setIsLoading ] = useState(false);
const [ errMessage, setErrMessage ] = useState('');
useEffect(() => {
setIsLoading(true);
// Calls API to get list of customers
getCustomers().then((response) => {
setCustomers(response.data.customers);
setIsLoading(false);
setErrMessage('');
}).catch((e) => {
setErrMessage(e.message);
setIsLoading(false);
});
}, []);
return [ customers, isLoading, errMessage ];
}

The component would simply imported into your component, just like a regular function:

import { useCustomers } from './customerHooks';...const [ customers, isLoading, errMessage ] = useCustomers();

Some additional points about custom hooks:

  • It is perfectly acceptable to have multiple layers of custom hooks to organize complex business logic
  • Custom hooks can be used by multiple components
  • Custom hooks can be unit tested independently with the React testing library

Create child components

Finally, if your component is returning a large chunk of JSX, then it is an indication that you should break the component down into smaller components. This:

return (
<div>
... // a whole bunch of code
{
customers.map((customer) => (
<div>
... // a whole bunch of code
</div>
))
}
</div>
);

could be trimmed down to this:

return (
<div>
... // a whole bunch of code
{
customers.map((customer) => (
<CustomerCard customer={customer} />
))
}
</div>
);

The child component would in turn have its own CSS, imported functions and custom hooks. A child component can also have its own child components and so on. Similar to hooks and functions, components that are small and logically structured can be more easily unit tested.

Keep files small

The React.js framework gives us a lot of tools to keep files small and unit-testable. Take advantage of that!

Finnovate.io is a technology company focused on helping organizations build unique digital experiences on web, mobile and blockchain. Finnovate.io offers development services, training, consulting, as well as a platform that rapidly turns paper based content into digital interactive experiences.

--

--

Alfred Yang
finnovate.io

Alfred is the founder of https://finnovate.io, a company that focuses on helping organizations build unique digital experiences on web, mobile and blockchain.