DEV Community

Prakhar Singh
Prakhar Singh

Posted on

Optimization, the React way

In this article, you'll learn about some of the best practices and how to optimize your React Web Application and create reusable components.

1.Debouncing and Throttling: These are both optimization concepts which limit rate off function calls(API calls, loggers) with a delay.

Debouncing:It takes a function and a delay and makes the function call only if the time interval between 2 calls is greater than delay. Use case: Search bar, scroll.

const debounce=function(fn,d){
let timer;
return function(){
 let context=this,args=arguments;
 clearTimeout(timer);
 timer=setTimeout(()=>{
   fn.apply(context,args)
   },d);
  }
};
Enter fullscreen mode Exit fullscreen mode

Throttling:It takes a function and a limit and makes the function call after the interval. Use case: Search bar, scroll.

const throttle=function(fn,limit){
let flag=true;
return function(){
 let context=this,args=arguments;
 if(flag){
 fn.apply(context,args);
 flag=false;
 setTimeout(()=>{
   flag=true;
   },limit);
  }
 }
};
Enter fullscreen mode Exit fullscreen mode

Instead of writing them on your own, one can leverage it from Lodash as well.

2.Component Composition: An alternative to prop drilling without using Redux or context API is Component composition. It relies heavily on the untamed power of children props.

export default function App() {
  const content = "Inner most passed prop";
 return (
    <div className="App">
      <Dashboard>
         <ComponentNeedingProps content={content}  />
      </Dashboard>
    </div>
  );
}

function Dashboard({ children }) {
  return (
    <div>
      <h3>I am the dashboard but i do not need props</h3>;
     { children }
    </div>
  );
}

function ComponentNeedingProps({ content }) {
  return <h3>{content}</h3>
}
Enter fullscreen mode Exit fullscreen mode

3.Custom Hooks: A reusable custom hook is used to avoid creating too many similar components that share the same logic. It also improves the code of your application by removing duplicate code, making your application easier to maintain. Let's create a custom hook named useToggle that returns a status state and a toggleStatus handler function:

import { useState, useCallback, useMemo } from "react";

const useToggle = () => {
  const [status, setStatus] = useState(false);

  const toggleStatus = useCallback(() => {
    setStatus((prevStatus) => !prevStatus);
  }, []);

  const values = useMemo(
    () => ({
      status,
      toggleStatus
    }),
    [status, toggleStatus]
  );

  return values;
};

export default useToggle;
Enter fullscreen mode Exit fullscreen mode

We can now reuse our new custom hook as many times as needed in any component that will take advantage of using this shared logic.

4.Optional Chaining and Nullish coalescing: Whenever you access a nested object property where you do not know if the sub object exists or not, we use && operator to check.

const nestedObject = {
    profile:{
      name: "Prakhar",
      details: {
          age: 22
}}}
if (nestedObject && nestedObject.profile && nestedObject.profile.details) {
    console.log(nestedObject.profile.details.age); // 22
}
Enter fullscreen mode Exit fullscreen mode

Now with optional chaining

console.log(nestedObject?.profile?.details?.age); //22
Enter fullscreen mode Exit fullscreen mode

Nullish coalescing operator (??) is a logical operator that returns its right-hand side expression when its left-hand side expression is null or undefined, and otherwise returns its left-hand side expression. Combining this with optional chaining we get:

console.log(nestedObject?.profile?.details?.address ?? "Address not specified"); //Address not specified
Enter fullscreen mode Exit fullscreen mode

5.Memoization: It is basically a form of optimization used mainly to speed up code by storing the results of expensive function calls and returning the stored result whenever the same expensive function is called again.

export function Sample({ name, age }) {
  return (
    <div>
      <div>Name: {name}</div>
      <div>Age: {age}</div>
    </div>
  );
}
// memoize the component
export const MemoizedSample = React.memo(Sample);

//Now when we call it
// On first render, React calls MemoizedSample function.
<MemoizedSample
  name="Alex"
  age=22
/>

// On next render, React does not call MemoizedSample function,
// preventing rendering as the props are same
<MemoizedSample
  name="Alex"
  age=22
/>

Enter fullscreen mode Exit fullscreen mode

6.Anonymous Functions: Avoid anonymous functions as they aren’t assigned an identifier (const/let/var), they aren’t persistent whenever a component inevitably gets rendered again. This causes JavaScript to allocate new memory each time this component is re-rendered, instead of allocating a single piece of memory only once, like when named functions are being used.

There are many other ways of improving the performance of your React application and many blogs on it as well. If you have any questions, feel free to ask.

Happy Coding!

Top comments (0)