I am new to React.js so I apologise if this is a foolish question or one that has been asked before. I have a ponent tree structured as such: form (grandparent) → input field (parent) → button (child)
I am attempting to listen for a click (onClick event handler) of the button and have that trigger a function at the level of the form (ideally also passing the content of the input field to the form).
Is there a general method to do this or a specific part of the documentation I should read? I know I can pass data from the parent to child via props, is there a reverse of this?
I am new to React.js so I apologise if this is a foolish question or one that has been asked before. I have a ponent tree structured as such: form (grandparent) → input field (parent) → button (child)
I am attempting to listen for a click (onClick event handler) of the button and have that trigger a function at the level of the form (ideally also passing the content of the input field to the form).
Is there a general method to do this or a specific part of the documentation I should read? I know I can pass data from the parent to child via props, is there a reverse of this?
Share Improve this question asked Feb 20, 2022 at 15:07 BenBen 5031 gold badge7 silver badges19 bronze badges 3-
Hi! Please update your question with a minimal reproducible example demonstrating the problem, ideally a runnable one using Stack Snippets (the
[<>]
toolbar button). Stack Snippets support React, including JSX; here's how to do one. It's impossible for aninput
field to have abutton
in it if you really meaninput
andbutton
. So an example would make things a lot clearer. – T.J. Crowder Commented Feb 20, 2022 at 15:09 - I answered a similar question earlier today. – よつば Commented Feb 20, 2022 at 15:14
- in terms of docs, it sounds like this could help: react docs and free code camp. – daniel Commented Feb 20, 2022 at 15:25
1 Answer
Reset to default 3You'd have the form pass a function to the child as a prop, and have the child pass that function on to the button as a prop, like this:
const {useState} = React;
const Example = () => {
const onClick = () => {
console.log("Clicked!");
};
return <Parent onClick={onClick} />;
};
const Parent = ({onClick}) => {
return <button type="button" onClick={onClick}>Click Me</button>;
};
ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare./ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
For that simple example, I've just recreated the function every time Example
is rendered, but there are times you don't want to do that, because it makes Parent
and button
both re-render any time anything in Example
changes. You might use useCallback
or useMemo
to avoid passing a new function to Parent
(and thus to button
) every time. (That would only help if Parent
and any other intermediate ponents were memoized, as with React.memo
or [for class
ponents] PureComponent
or a ponent using shouldComponentUpdate
.) More about that in my other answers here and here.
Here's something demonstrating how changing something unrelated to the click handler causes unnecessary re-rendering of Parent
:
const { useState, useEffect } = React;
const Example = () => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const handle = setInterval(
() => setCounter(c => c + 1),
800
);
return () => {
clearInterval(handle);
};
}, []);
const onClick = () => {
console.log("Clicked!");
};
return <div>
<div>
Counter: {counter}
</div>
<Parent onClick={onClick} />
</div>;
};
const Parent = ({onClick}) => {
console.log(`Parent rendering...`);
return <button type="button" onClick={onClick}>Click Me</button>;
};
ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare./ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
And an example that memoizes onClick
and uses React.memo
to make Parent
not re-render when nothing significant (to it) changes:
const { useState, useEffect, useCallback } = React;
const Example = () => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const handle = setInterval(
() => setCounter(c => c + 1),
800
);
return () => {
clearInterval(handle);
};
}, []);
const onClick = useCallback(() => {
console.log("Clicked!");
}, []);
return <div>
<div>
Counter: {counter}
</div>
<Parent onClick={onClick} />
</div>;
};
const Parent = React.memo(({onClick}) => {
console.log(`Parent rendering...`);
return <button type="button" onClick={onClick}>Click Me</button>;
});
ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare./ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>