SPG
SPG

Reputation: 6197

How to call Parent function with useState Hook in React?

The ideal result is displaying strings when items in the list are clicked.

Here's the Root Component.

const App = () => {
  const [click, setClick] = useState(null);
  const handleClick = name => {
    setClick(name);
  };
  return (
    <div>
      <Parent handleClick={handleClick} />
      {click && <p>{click} is clicked.</p>}
    </div>
  );
};

and Parent Component.

const Parent = ({ handleClick }) => (
  <div>
    <Child
      name="First Item"
      handleClick={handleClick("First Item is Clicked!")}
    />
    <Child
      name="Second Item"
      handleClick={handleClick("Second Item is Clicked!")}
    />
    <Child
      name="Third Item"
      handleClick={handleClick("Third Item is Clicked!")}
    />
  </div>
);

and Child Component.

const Child = ({ name, handleClick }) => <li onClick={handleClick}>{name}</li>;

Also, the code sandbox link.

I just wondered why the result never is never changed by click.

Upvotes: 4

Views: 11389

Answers (2)

Shubham Khatri
Shubham Khatri

Reputation: 281696

Problem is that you aren't passing a function to handleClick instead an evaluated value from parent. You code must be

import React from "react";
import Child from "./Child";

const Parent = ({ handleClick }) => (
  <div>
    <Child
      name="First Item"
      handleClick={() => handleClick("First Item is Clicked!")}
    />
    <Child
      name="Second Item"
      handleClick={() => handleClick("Second Item is Clicked!")}
    />
    <Child
      name="Third Item"
      handleClick={() => handleClick("Third Item is Clicked!")}
    />
  </div>
);

export default Parent;

Working demo

Upvotes: 9

Ori Drori
Ori Drori

Reputation: 191976

You are calling the handleClick() function instead of passing a function to the Child components. Use an inline arrow function to wrap the call.

Note: since the arrow functions will be recreated whenever parent is rendered, this might cause a performance issue, if you've got many items, or your render the parent constantly for other reasons.

const { useState } = React;

const App = () => {
  const [click, setClick] = useState(null);
  const handleClick = name => {
    setClick(name);
  };
  return (
    <div>
      <Parent handleClick={handleClick} />
      {click && <p>{click} is clicked.</p>}
    </div>
  );
};

const Parent = ({ handleClick }) => (
  <div>
    <Child
      name="First Item"
      handleClick={() => handleClick("First Item is Clicked!")}
    />
    <Child
      name="Second Item"
      handleClick={() => handleClick("Second Item is Clicked!")}
    />
    <Child
      name="Third Item"
      handleClick={() => handleClick("Third Item is Clicked!")}
    />
  </div>
);

const Child = ({ name, handleClick }) => <li onClick={handleClick}>{name}</li>;

ReactDOM.render(
  <App />,
  root
)
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

Another solution is to pass both functions, and text to the Child component, and let it handle the function call. In this case you can memoize the function with useCallback:

const { useState, useCallback } = React;

const App = () => {
  const [click, setClick] = useState(null);
  const handleClick = name => {
    setClick(name);
  };
  return (
    <div>
      <Parent handleClick={handleClick} />
      {click && <p>{click} is clicked.</p>}
    </div>
  );
};

const Parent = ({ handleClick }) => (
  <div>
    <Child
      name="First Item"
      handleClick={handleClick} text="First Item is Clicked!"
    />
    <Child
      name="Second Item"
      handleClick={handleClick} text="Second Item is Clicked!"
    />
    <Child
      name="Third Item"
      handleClick={handleClick} text="Third Item is Clicked!"
    />
  </div>
);

const Child = ({ name, handleClick, text }) => {
  const handler = useCallback(() => handleClick(text), [handleClick, text]);
  
  return <li onClick={handler}>{name}</li>;
}

ReactDOM.render(
  <App />,
  root
)
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

Upvotes: 5

Related Questions