Ben
Ben

Reputation: 493

React – Passing event from child component to parent

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

Answers (1)

T.J. Crowder
T.J. Crowder

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

Related Questions