Avoid passing inline functions as a prop in React.

Ishwar Rimal
5 min readSep 19, 2023

--

When it comes to React, it’s safe to say that inline functions are evil.

Stop passing an inline function as a prop in React.

Regarding the above video, it is my first attempt at making a video. I realized explaining things over a video is easier and better compared to writing an article. Would really appreciate any feedback.

I recommend reading the entire article (or watching the video), but for those who want TLDR, here it is

TLDR,; On every re-render of a component, a function gets re-created, and if it’s passed as a props to a child component, it will cause the child to be re-rendered every time the parent rendrs.

Let’s try to understand this more in detail:

Let’s look at this sample React code, which calls a Child component within App

import React, { useState } from "react";
import "./styles.css";

export default function App() {
const [count, setCount] = useState(1);
return (
<div className="App">
<p>Value of count is {count}</p>
<Child />
</div>
);
}

function Child() {
console.log("Child is getting rendered");
return <h1>I am a child</h1>;
}

I have added console.log on the Child component to check the number of times this component gets called.

As expected, with the above code, the child gets rendered only once.

Now, let’s make a slight change to the code.

Let’s add a method to increment the count:

import React, { useState } from "react";
import "./styles.css";

export default function App() {
const [count, setCount] = useState(1);
return (
<div className="App">
<p>Value of count is {count}</p>
<Child />
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

function Child() {
console.log("Child is getting rendered");
return <h1>I am a child</h1>;
}

In this case when I click Increment, along with the count getting incremented, the Child component gets re-rendered. This happens because of the working of diffing alogirthm (read more in my free GitHub repo for an interview preparation guide)

Let’s try to solve that using React.memo , a very useful tool provided by React to memoize a component.

import React, { useState } from "react";
import "./styles.css";

export default function App() {
const [count, setCount] = useState(1);
return (
<div className="App">
<p>Value of count is {count}</p>
<MyChild />
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

function Child() {
console.log("Child is getting rendered");
return <h1>I am a child</h1>;
}

const MyChild = React.memo(Child);

In this code, I have wrapped the Child component with React.memo and I am now using MyChild component in the App.

With this, I have achieved memoization of the Child component and no matter how many times my App component re-renders, the child is not going to re-render…unless…

Unless I pass a prop to the Child component. Let’s pass a callback function as a prop to the Child component.

import React, { useState } from "react";
import "./styles.css";

export default function App() {
const [count, setCount] = useState(1);
const callback = () => {}
return (
<div className="App">
<p>Value of count is {count}</p>
<MyChild callback={callback} />
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

function Child() {
console.log("Child is getting rendered");
return <h1>I am a child</h1>;
}

const MyChild = React.memo(Child);

In the above code, I am passing an empty function as a prop to the Child component.

What happens now when I increment the counter?

As you can see even though theChild is doing nothing with the function passed, yet, the child component gets re-rendered every time the parent component re-renders.

This is because every time a component re-renders, any function within it gets re-created. (read more about this here)

How can we solve this?

React provides a hook called useCallback to solve such issues. Let’s update our code to make it more efficient.

import React, { useState, useCallback } from "react";
import "./styles.css";

export default function App() {
const [count, setCount] = useState(1);
const callback = useCallback(() => {}, []);
return (
<div className="App">
<p>Value of count is {count}</p>
<MyChild callback={callback} />
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

function Child() {
console.log("Child is getting rendered");
return <h1>I am a child</h1>;
}

const MyChild = React.memo(Child);

In the above code, you can see that I have wrapped the callback function with useCallback hook provided by React.

Now when I increment the counter, the Child component doesn’t get re-rendered. The reason is that useCallback memoizes the function. (I will not be going into detail about the usage of useCallback here.)

Now, let’s come to the most important part of this blog. As you can see in the above code, we have two functions in our App component, one being passed as a prop to Child and the other to onClick for the button.

If you closely look into both, you will realize that there is a difference in the way both are used. One is passed as a function and in other, we’re using an inline function.

In terms of functionality, there is not much difference between both. I could rewrite the callback function as an inline function and still achieve the same functionality.

import React, { useState, useCallback } from "react";
import "./styles.css";

export default function App() {
const [count, setCount] = useState(1);
const callback = useCallback(() => {}, []);
return (
<div className="App">
<p>Value of count is {count}</p>
<MyChild callback={() => callback()} />
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

function Child() {
console.log("Child is getting rendered");
return <h1>I am a child</h1>;
}

const MyChild = React.memo(Child);

But, unknowingly we’ve introduced a bug in terms of performance.

Even though the callback function is memoized, the anonymous function passed as a prop to the Child component is not memoized, hence, every time the App re-renders the Child gets re-rendered.

As you can see, it may not affect the performance for the case like updating a state, but it definitely affects the performance when passing as a prop.

This is a very subtle yet impactful change that if not made carefully, we can lose all the performance enhancement that we would be expecting otherwise by making use of useCallback react.memo and other tools.

I hope you found this article useful. I would love to hear your thoughts. 😇

Thanks for reading. 😊

Cheers! 😃

If you find this article useful, you can show your appreciation by clicking on the clap button. As the saying goes, ‘When we give cheerfully and accept gratefully, everyone is blessed’.

--

--

Ishwar Rimal

Senior FrontEnd Engineer at Intuit. 8 years experience. I write articles on JavaScript, React, Web Optimisation, Startups, Work Life Balance, etc. ❤️ JavaScrip