Reputation: 6197
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
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;
Upvotes: 9
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