ReactHustle

How to Pass Function as Props in React Functional Component

Jasser Mark Arioste

Jasser Mark Arioste

How to Pass Function as Props in React Functional Component

Hello! In this tutorial, you'll learn how to pass a function as props in a react functional component. 

Introduction #

We usually want to pass functions in react as handlers or callbacks for an event or user input. However, if you're new to React, you might be wondering why the onClick() callback doesn't work, there are a few possible reasons and we'll discuss them in this tutorial. 

Incorrect way of Passing a Function as Props #

Usually, it's straightforward to pass functions as props in a react functional component. We only have to pass a function. For example:

const ComponentA = () => {
  const handleClick = () => {
    console.log(`Button is clicked`);
  };

  return <div>
    <button onClick={handleClick}>My Button</button>
  </div>;
};
export default ComponentA;
12345678910

However, one of the most common mistakes is passing functions with parameters. Beginners usually call/execute the callback function and pass the arguments with it which is incorrect. This is especially true if you're not using typescript. For example:

const ComponentA = () => {
  const handleClick = (name: string) => {
    console.log(`Button ${name} is clicked`);
  };

  return <div>
    <button onClick={handleClick("my-button")}>My Button</button>
  </div>;
};
export default ComponentA;
12345678910

In this example, we want to log the name of the button. But the problem is, we are already executing/calling the function which returns void. Using typescript will result in a type error: Type 'void' is not assignable to type 'MouseEventHandler<HTMLButtonElement> | undefined'.

There are two ways to fix this:

  1. Wrapping the call in an anonymous function.
  2. Modify handleClick so that it returns a function instead of void.

Solution 1: Wrapping the call in an anonymous function

Oftentimes, it is sufficient to wrap the handler in an anonymous function. To wrap the call in an anonymous function, we can just do the following.

const ComponentA = () => {
  const handleClick = (name: string) => {
    console.log(`Button ${name} is clicked`);
  };

  return (
    <div>
      <button
        onClick={() => {
          handleClick("my-button");
        }}
      >
        My Button
      </button>
    </div>
  );
};
export default ComponentA;
123456789101112131415161718

Solution 2: Modifying handleClick so that it returns a function

This is also an option but I don't recommend it since it makes the code more unreadable than the previous solution. For example:

const ComponentA = () => {
  const handleClick = (name: string) => {
    return function () {
      return console.log(`Button ${name} is clicked`);
    };
  };

  return (
    <div>
      <button onClick={handleClick("my-button")}>My Button</button>
    </div>
  );
};
export default ComponentA;
1234567891011121314

Passing Function when Iterating an Array #

Another scenario is when using Array.map to iterate some array of objects and pass a function event handler. For example, suppose we have a <TodoList/> component that lists an array of todo's and once we click the delete button, it should remove that specific Todo.

Incorrect way

import { useState } from "react";

const TodoList = () => {
  const [todos, setTodos] = useState([
    {
      id: 1,
      description: "Do Exercise",
      completed: false,
    },
    {
      id: 2,
      description: "Cook food",
      completed: false,
    },
  ]);

  //delete handler
  const handleDelete = (id: number) => {
    setTodos(todos.filter((todo) => todo.id !== id));
  };
  return (
    <div>
      TodoList
      <div>
        {/* inside Array.map */}
        {todos.map((todo) => {
          return (
            <div key={todo.id}>
              <p>
                {todo.description} -
                <button onClick={handleDelete(todo.id)}>Delete</button> {/*Incorrect since handleDelete is executed */}
              </p>
            </div>
          );
        })}
      </div>
    </div>
  );
};
export default TodoList;

12345678910111213141516171819202122232425262728293031323334353637383940

In Line 31, this is incorrect since handleDelete is executed which returns void. To fix this we have to wrap it in a function.

Correct way

<div key={todo.id}>
  <p>
    {todo.description} -
    <button onClick={()=>handleDelete(todo.id)}>Delete</button> 
  </p>
</div>
123456

Passing a Function in useCallback #

Sometimes, we need to use the useCallback hook to avoid re-creating the function every time the component re-renders. To implement this we just have to wrap our existing function in a useCallback hook. We can also preserve its parameters.

import { useCallback, useState } from "react";

const TodoList = () => {
  const [todos, setTodos] = useState([
    {
      id: 1,
      description: "Do Exercise",
      completed: false,
    },
    {
      id: 2,
      description: "Cook food",
      completed: false,
    },
  ]);

  const handleDelete = useCallback(
    (id: number) => {
      setTodos(todos.filter((todo) => todo.id !== id));
    },
    [todos]
  );

  return (
    <div>
      TodoList
      <div>
        {/* inside Array.map */}
        {todos.map((todo) => {
          return (
            <div key={todo.id}>
              <p>
                {todo.description} -
                <button onClick={() => handleDelete(todo.id)}>Delete</button>
              </p>
            </div>
          );
        })}
      </div>
    </div>
  );
};
export default TodoList;
12345678910111213141516171819202122232425262728293031323334353637383940414243

As you can see, we didn't change anything when we used the function in line 34. This improves performance if you have a lot of states in this component that can trigger a re-render.

Conclusion #

You learned how to pass functions as props in a react functional component. You learned what not to do, and what to do in different scenarios when passing functions in event handlers.

If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on Twitter.

Resources #

Credits: Image by Frank Winkler from Pixabay

Share this post!

Related Posts

Disclaimer

This content may contain links to products, software and services. Please assume all such links are affiliate links which may result in my earning commissions and fees.
As an Amazon Associate, I earn from qualifying purchases. This means that whenever you buy a product on Amazon from a link on our site, we receive a small percentage of its price at no extra cost to you. This helps us continue to provide valuable content and reviews to you. Thank you for your support!
Donate to ReactHustle