BlogReactJS

3 ways to pass a function as a prop in React

Written by Codemzy on September 15th, 2023

Here are three ways you can pass a function as a prop in React, with and without parameters - all while avoiding infinite loops!

You can give React components props, which are like inputs to give your component some extra information (or functions!) to work with.

You can pass all sorts of props to your components. Strings. Numbers. Booleans. Objects. And, as you have probably guessed from the title of this blog post... functions!

<YourComponent 
  aString="amazing" 
  aBoolean={true} 
  aNumber={3} 
  anObject={{ key: "value" }}
  aFunction={() => { return "I'm a function!" }}
/>

Passing functions is one of the more useful props because you can get your component to do something. And you can use the same component over and over, but give it a different function so that each time you use it, it does something different.

Pretty cool!

In the example above, I'm passing an inline function. But that's not the only way to pass a function as a prop.

So let's look at some more options:

  • As a function yourProp={yourFunction}
  • As an inline function yourProp={() => alert("The function has been called!")}
  • As an inline function with parameters yourProp={() => yourFunction(id, name)}

1. As a function

You can pass a function as a prop by naming the function in your component and then giving the function as the prop value. Like this:

// the function
function yourFunction() {
  alert("The function has been called!");
};

return <YourComponent action={yourFunction} />

Whatever you do, don't call the function when you pass it as a prop:

// the function
function yourFunction() {
  alert("The function has been called!");
};

// ❌ don't call the function in the prop
return <YourComponent action={yourFunction()} />

That won't work.

Every time your component renders, the function will be called, and because the prop has changed, the component will render again, calling the function again... and that's what we call an infinite loop!

Just pass the function name, so your component can call it when it is good and ready (and not a moment sooner!).

Here's how you could useEffect to call the function when the component mounts.

import React from 'react';

function ParentComponent(props) {
  // the function
  function yourFunction() {
    alert("The function has been called!");
  };

  return <ChildComponent action={yourFunction} />
};

function ChildComponent({action}) {
  
  // call the function once on mount
  React.useEffect(() => {
    action();
  }, []);
  
  return "I'm the child component!";
};

The function is defined in the parent component, passed as a prop to the child component, and called by the child component.

A real-world example of this could be an onClick handler passed to a Button component.

Let's imagine we have a reusable Button component we use throughout our React app for consistent styling.

<Button>Click Me!</Button>

Each time we use that Button we want it to look the same (or similar), but each button might do something different.

For example, one button might submit a form, another might "like" a post, and another might open a modal... you get the idea!

Here's how our Button component might be coded*:

*This is a very simplified version, just showing how to pass props to the button!

function Button(props) {
  return (
    <button {...props} />
  );
};

All of the props we send to our Button component are given to the button, with{...props}, so we can pass a function for the onClick handler.

Alternatively, you can deconstruct props, and pass the onClick directly in the component like this:

function Button({ children, onClick }) {
  return (
    <button onClick={onClick}>{children}</button>
  );
};

Whichever way you choose to pass that onClick handler - with{...props} or onClick - it ends up on the button.

So now let’s pass our function!

function Parent() {
  
  // the function
  function yourFunction() {
    alert("The function has been called!");
  };
  
  return (
    <Button onClick={yourFunction}>Click Me!</Button>
  )
};

Whenever you click the button, your function gets called!

<div id="app"></div>
// button
function Button(props) {
  return (
    <button className="p-3 rounded bg-gray-200" {...props} />
  );
};

// app
function App() {
  
  // the function
  function yourFunction() {
    alert("The function has been called!");
  };
  
  return (
    <div className="flex flex-col w-full min-h-screen items-center justify-center p-5">
      <Button onClick={yourFunction}>Click Me!</Button>
    </div>
    )
}



ReactDOM.render(
  <App />,
  document.getElementById('app')
);

2. As an inline function

You might not always want to declare a function and then pass the function name. For a start, you need to come up with a name for the function. It could match the prop name, like if the prop is called action you could call it onAction - but on the action of what?

If the function is simple and doesn't need to be reused or called from lots of different places, you could pass an anonymous inline function instead.

Let’s keep our earlier example, but instead of passing the handleClick function, we’ll give the prop an inline function to use.

Like this:

function Parent() {
  
  return (
    <Button onClick={() => alert("The function has been called!")}>Click Me!</Button>
  )
};

3. As an inline function with parameters

Do you know when inline functions are really useful? When you need to call a function with parameters.

You can't pass parameters with a function. So let's say you had a function like this:

function sayMyName(name) {
  alert(`Your name is ${name}`);
};

How can I get the Button component to work?

// ❌ Your name is [object Object]
<Button onClick={sayMyName}>Click Me!</Button>
// ❌ calls the function on render
<Button onClick={sayMyName("codemzy")}>Click Me!</Button>
// 🥴 the button component needs to know about the name prop
<Button onClick={sayMyName} name="codemzy">Click Me!</Button>
// ✅ wrap in an inline function
<Button onClick={() => sayMyName("codemzy")}>Click Me!</Button>

This is super useful if you have a list of components and each one needs to call the same function but with different parameters.

Like this:

let users = [
  { id: 1, name: "Alan" },
  { id: 2, name: "Beth" },
  { id: 3, name: "Charlie" },
  { id: 4, name: "Donna" },
  { id: 5, name: "Erica" },
  { id: 6, name: "Freddie" },
];

function sayMyName(name) {
  alert(`Your name is ${name}`);
};

return (
  { names.map(name => {
    return <Button key={user.id} onClick={() => sayMyName(user.name)}>{user.name}</Button>;
  })}
);

In this example, we are rendering an array of objects in React. And pass the sayMyName function as the onClick prop (like we were before).

But since each button belongs to a different name, we are wrapping the sayMyName function in an anonymous inline function so that we can pass the user.name parameter.

Now each button calls the function with a different name!

What about the key prop? Since it's a list, we are also being good React citizens (and avoiding errors!) by passing a unique key prop for each item!

<div id="app"></div>
// button
function Button(props) {
  return (
    <button className="p-3 m-1 rounded bg-gray-200" {...props} />
  );
};

// app
function App() {
  let users = [
    { id: 1, name: "Alan" },
    { id: 2, name: "Beth" },
    { id: 3, name: "Charlie" },
    { id: 4, name: "Donna" },
    { id: 5, name: "Erica" },
    { id: 6, name: "Freddie" },
  ];
  
  // the function
  function sayMyName(name) {
    alert(`Your name is ${name}`);
  };
  
  return (
    <div className="flex flex-col w-full min-h-screen items-center justify-center p-5">
      { users.map(user => {
        return <Button key={user.id} onClick={() => sayMyName(user.name)}>{user.name}</Button>;
      })}
    </div>
  );
};



ReactDOM.render(
  <App />,
  document.getElementById('app')
);