10 ways to optimize a React codebase

10 ways to optimize a React codebase

Table of contents

Recently, I was given a task to review a codebase in React and find ways to make the project more performant. Here are 10 common things I would typically look for when trying to improve performance and readability.

  1. Not utilizing lazy import/code-splitting:

    • Lazy loading or code splitting helps in reducing the initial bundle size and improving the application's load time by loading JavaScript modules only when they are needed. Failure to utilize lazy imports can result in unnecessarily large initial downloads.
    // Without lazy import
    import SomeComponent from './SomeComponent';

    // With lazy import
    const SomeComponent = React.lazy(() => import('./SomeComponent'));
  1. Not cleaning up event listeners in useEffect:

    • Failing to clean up event listeners or subscriptions in the useEffect cleanup function can lead to memory leaks in the application.
    javascriptCopy codeuseEffect(() => {
      const handleClick = () => {
        // Handle click event
      };

      document.addEventListener('click', handleClick);

      return () => {
       // Always clear event listeners/timeouts in here
        document.removeEventListener('click', handleClick);
      };
    }, []);
  1. Big unoptimized images:

    • Large and unoptimized images can significantly impact page load times. Always optimize images for the web by compressing them and serving them in the appropriate size and format.

    • Use image optimization tools like tinypng.

  2. Not using useCallback/useMemo for unnecessary re-renders:

    • Failing to memoize functions or values using useCallback and useMemo can result in unnecessary re-renders of components.
    javascriptCopy codeconst memoizedCallback = useCallback(() => {
      // Function body
    }, [/* Dependencies */]);

    const memoizedValue = useMemo(() => {
      // Value computation
    }, [/* Dependencies */]);
  1. Improper use of dependency arrays:

    • Incorrectly specifying dependencies in the dependency array of useEffect, useCallback, or useMemo hooks can lead to unexpected behavior or bugs.

    • Ensure all dependencies are included in the dependency array and avoid using the empty dependency array if your effect relies on any state or props.

    • Also remember to memoize the objects/arrays/functions itself that are in the dependency array as it will create new references which will cause re-renders

  2. Not caching data:

    • Failing to cache data can result in unnecessary network requests and decreased application performance. Utilize caching mechanisms like localStorage, sessionStorage, or caching libraries such as react-query.
  3. No infinite scroll/pagination/virtualization:

    • Loading all data at once without implementing infinite scroll, pagination, or virtualization can result in poor performance, especially with large datasets. Implementing these techniques can improve user experience and reduce load times.

    • Example: Implement infinite scroll using libraries like react-infinite-scroll-component or react-virtualized or pagination using server-side pagination techniques.

  4. Inefficient API calls:

    • Inefficient API calls, such as making redundant or unnecessary requests, can negatively impact performance and increase server load. Optimize API calls by batching requests, implementing caching, or using optimized query parameters.

    • React-query for more efficient data fetching or debounce/throttle requests to prevent unnecessary server load.

  5. Inefficient component design:

    • Poorly designed components with excessive re-renders or unnecessary state can lead to performance issues. Optimize component design by breaking down complex components into smaller ones, using memoization, and avoiding unnecessary state.

    • Example: Refactor complex components into smaller, reusable ones and utilize React's context API for state management and react-query for async state management.

  6. Adding unnecessary libraries:

    • Including unnecessary libraries in the project can bloat the bundle size and increase load times. Evaluate whether a library is truly necessary for your project and consider alternatives or custom solutions.

    • Eg, installing whole lodash or installing more than one library that achieves the same functionality

    • Stick with one UI library that will cover all of your needs

Conclusion

By following these best practices and optimizing your React codebase, you can create high-performance, maintainable, and user-friendly applications that provide a seamless experience for your users. I will go in details over some of the techniques in future posts. Remember that optimization is an ongoing process, and continuously monitoring and refining your codebase is essential for ensuring optimal performance as your project evolves.

Thank you for reading, let me know your thoughts! And share more tips in the comments!