Reputation: 493
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 component 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?
Upvotes: 3
Views: 10242
Reputation: 1073968
You'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.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/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 components were memoized, as with React.memo
or [for class
components] PureComponent
or a component 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.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/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.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
Upvotes: 3