This is the function where I am passing the onClick prop (setShowModal is setState() from the useState hook):
<MyFunctionalComponent
onClick={() => setShowModal(true)}
...other props here
/>
This is the functional component that receives the prop:
export const MyFunctionalComponent = ({ onClick }) => {
return (
<section>
...other code here
{onClick && (<Button>{ctaText}</Button>)}
</section>
);
};
But the Button component never appears, because the prop onClick is undefined. When I console.log the prop inside the functional component, it initially prints the function in the console, but then prints two more times as undefined. Could someone explain why that would be? I got it to work by spreading ...props instead. But the console.log remains the same? I don't understand why. This is my first question on Stack Overflow, so feel free to give me feedback on how to ask better questions :)
The reason why you are receiving an 'undefined' response is because as @Zrogua mentioned, onClick
is an event listener function rather than a persistent value (like state you define).
import React from "react";
const YourButton = ({ onClick }) => {
console.log(onClick);
return <section>{onClick && <button>here</button>}</section>;
};
const ParentDiv = () => {
return (
<div>
<h1>Button Props</h1>
<h2>Start editing to see some magic happen!</h2>
<YourButton onClick={() => console.log("CLICK")} />
</div>
);
};
export default ParentDiv;
Result of console.log()
:
function onClick() // index.js:27:25
The reason why this is because props are read-only. From the React Docs:
Whether you declare a component as a function or a class, it must never modify its own props ... Such functions are called “pure” because they do not attempt to change their inputs, and always return the same result for the same inputs.
Therefore your button will only show if the onClick
function is defined. For example, if you did not give onClick
a function or value, the button will not appear:
import React, { useState } from "react";
const YourButton = ({ onClick }) => {
console.log(onClick);
return (
<section>
{onClick && <button>This button is shown if a button is defined.</button>}
</section>
);
};
const ParentDiv = () => {
return (
<div>
<h1>Button Props</h1>
<YourButton onClick={() => console.log("CLICK")} />
<YourButton /> {/* You won't see this button because the function is not defined. */}
</div>
);
};
export default ParentDiv;
The button appears because the prop has a value that is not undefined (your onClick
function), and because it is read-only, you cannot access that function in your child component.
Instead, (1) define the modal state in the parent component and (2) pass the state through props to the button like so:
import React, { useState } from "react";
const YourButton = ({ onClick }) => {
console.log(onClick);
return (
<section>
{onClick && <button>This button is shown if a button is defined.</button>}
</section>
);
};
const AltButton = ({ modal }) => {
return (
<section>
{modal && (
<button>This button is shown the modal state is passed.</button>
)}
</section>
);
};
const ParentDiv = () => {
const [modal, setModal] = useState(false);
return (
<div>
<h1>Button Props</h1>
<YourButton onClick={() => console.log("CLICK")} />
<YourButton />{" "}
{/* You won't see this button because the function is not defined. */}
<section>
<button onClick={() => setModal(!modal)}>OPEN MODAL</button>
</section>
{modal && <p>this is dependent on state</p>}
<AltButton modal={modal} />
</div>
);
};
export default ParentDiv;
Working CodeSandbox: https://codesandbox.io/s/stack-66715327-passingfunctions-92pzr
Finally, if I am reading between the lines and understanding correctly that you are looking to hide a button when a modal is open, here is a little modal wrapper trick I use for buttons that open modals: https://codesandbox.io/s/stack-66715327-modalwrapper-wvl54