3 Lesser-known React Functional Component Optimization Techniques

Reg
3 min readJan 5, 2023

--

Photo by Maarten Deckers on Unsplash

If you stumble upon this post, chances are, you are already familiar with some of the more common React optimization techniques, such as useCallback, useMemo and React.memo to reduce the number of unnecessary re-rendering performed; react-window and react-lazyload libraries to lazy load large amount of items in a list / table or images, to avoid unused off-screen DOM elements from being created.

But in this article, we are going to focus on some of the lesser-known React optimization techniques (well, at least I don’t recall seeing them much on the web).

  1. Lazy initial state
const [state, setState] = useState(expensiveFn());

Normally, when you have a computationally expensive function to return the initial state result, the same function will run for any subsequent re-render caused by the state dispatch function.

This is suboptimal because the expensive function is only needed to run for the initial state result right?

const [state, setState] = useState(() => expensiveFn());

To solve the issue, you may provide an arrow function that runs the expensive function instead. Now React will only call the expensive function during first render, and creates a new function for subsequent re-renders (note that we are NOT running the expensive function inside the arrow function anymore, and creating a new function is far cheaper than calling what is inside of it).

const [state, setState] = useState(() => 'Hello World!');

However, note that if the initial state is not involving some heavy computation, then it’s ok to not use lazy initialization, as it’s not that expensive to re-create the same value during re-renders.

2. React.fragment to avoid adding extra HTML tags

function Example() {
return (
<div>
<h1>Hello</h1>
<p>medium is the best!</p>
</div>
);
}

React functional component can only return a single HTML element, thus sometimes we have to add a new pair of HTML tags (e.g <div> tags) to enclose all other elements to fulfill React’s rule, this however introduces another tag to re-render.

However, we can replace the extra HTML tags with React fragment, which does not introduce any extra tag to render and fulfill React’s rule at the same time.

The syntax goes like this :

function Example() {
return (
<>
<h1>Hello</h1>
<p>medium is the best!</p>
</>
);
}

or alternatively :

function Example() {
return (
<React.fragment>
<h1>Hello</h1>
<p>medium is the best!</p>
</React.fragment>
);
}

3. State colocation

State colocation in React refers to keeping the states as close as to where they are actually needed, but what does it have to do with optimization?

function Example() {
const [text, setText] = useState(0);

function onChange(e) {
setText(e.target.value)
}

return (
<>
<SimpleInput text={text} onChange={onChange} />
<ExpensiveComponent />
</>

)

}

function SimpleInput({text, onChange}) {
return (
<>
<input value={text} onChange={(e) => onChange(e)} />
</>
);
}

function ExpensiveComponent({}) {
const expensiveFn = () => {
for (let i = 0; i < 100000000; i++) {
// nothing
}
};

expensiveFn();

return <div>slow</div>;
}

Take the above code for example, every time the state ‘text’ is updated in the parent component, React will have to go through all of its child components (i.e ‘SimpleInput’ and ‘ExpensiveComponent’ components) to check if DOM manipulation is needed or not, this reduces performance significantly if you have a super expensive component like the ‘ExpensiveComponent’ component.

To avoid unnecessary checking on the expensive child component, we can co-locate the ‘text’ state to ‘SimpleInput’ component, where it is actually needed, just like the code example below :

function Example() {
return (
<>
<SimpleInput />
<ExpensiveComponent />
</>
)
}

function SimpleInput({text, onChange}) {
const [text, setText] = useState(0);

function onChange(e) {
setText(e.target.value)
}

return (
<input value={text} onChange={(e) => onChange(e)} />
);
}

function ExpensiveComponent({}) {
const expensiveFn = () => {
for (let i = 0; i < 100000000; i++) {
// nothing
}
};

expensiveFn();

return <div>slow</div>;
}

Wrapping up

This sums up the 3 techniques for React optimization, hope you learn something today, and any suggestions for improvement are welcomed!

--

--