Here's Why You Need To Avoid Derived States in ReactJS
Learn how Derived States can cause performance issues and re-renders
Introduction
ReactJS, a popular JavaScript library for building user interfaces, emphasizes the importance of state management in creating interactive UIs. However, when managing state, one of the common pitfalls developers fall into is using derived state.
What exactly is a Derived State?
As the name suggests, the derived states are the states which are derived from props. In other words, when a state uses props in the form of its default value, then, the state is called a derived state.
What are the issues with Derived States?
As one can observe, using the Derived States lead to Two Sources of Truth inside a single component. Originally, the props acted as a single Source of Truth for the component. When a derived state is created, it gets diverged from the props and becomes out of sync. It can lead to issues such as -
Unwanted re-renders
Accidental props mutation
Performance concerns
Multiple Sources of Truth and many other anti-patterns
Increased Complexity
Solutions
1. Compute Values Inline
Instead of storing derived values in the state, compute them directly inside the render or return statements. For instance, in the MyComponent
example, you can simply use items.length
without storing it in state:
function MyComponent({ items }) {
return <div>Total items: {items.length}</div>;
}
This keeps your component’s state minimal, avoids unnecessary renders, and ensures the UI is always in sync with the data.
2. Use Memoization for Expensive Calculations
If the derived value is expensive to compute (e.g., running a filter or sort operation on a large array), you can use the useMemo
hook to cache the result and avoid recomputing it unnecessarily.
function MyComponent({ items }) {
const itemCount = useMemo(() => items.length, [items]);
return <div>Total items: {itemCount}</div>;
}
Here, useMemo
ensures that the items.length
is only recalculated when the items
prop changes, preventing unnecessary calculations on each render.
3. Lift State Up
If you find yourself needing to sync derived state with multiple components, consider lifting the state up. Move the source of truth higher up the component tree and pass it down via props to the components that need it. This ensures that all components share a single, consistent state.
4. Use Custom Hooks for Shared Logic
For complex derived state logic that’s needed across multiple components, you can encapsulate the logic into custom hooks. This allows you to reuse the logic without duplicating state management or cluttering your components.
function useItemCount(items) {
return useMemo(() => items.length, [items]);
}
function MyComponent({ items }) {
const itemCount = useItemCount(items);
return <div>Total items: {itemCount}</div>;
}
By using custom hooks, the derived logic is abstracted, making your components simpler and more focused on their main responsibilities.
Conclusion
Derived state in React often introduces more problems than it solves. It can lead to inconsistencies, unnecessary re-renders, and increased complexity in your code. Instead, favor direct computations, memoization, lifting state up, or using custom hooks to avoid these issues. By adhering to React’s core principle of a single source of truth and keeping your state management lean, you can ensure that your applications remain efficient, maintainable, and bug-free.
Avoiding derived state is a best practice that will help you build more predictable and performant React applications!