Search code examples
javascriptjqueryreactjscomponents

React - Change state in child component from another child component


I need to change the state in one child component when a button is clicked in another child component. Both the childs have the same parent component.

import React from "react":
import A from "...";
import B from "...";

class App extends React.Component {
    render() {
        <div>
            <A />
            <B /> 
        </div>
    }
}

In this example, when a button in component A is pressed, the state in component B needs to be changed.


Solution

  • This application sounds like the perfect use case for "Lifting State Up", i.e. holding the main state in the parent component. Then you basically just pass down handlers (to change the parent state) to component A (this becomes the button's onClick handler), then pass down the state you want to show to component B.

    When you click the button, the setState is called in the parent component, which automatically re-renders all children whose props change (including component B).

    Here's more detailed info: https://reactjs.org/docs/lifting-state-up.html

    EDIT: The reply below reminded me that I should probably add some code to illustrate - but I've made a few changes that simplify things.

    import React, {useState} from "react":
    import A from "...";
    import B from "...";
    
    const App = () => {
        const [show, setShow] = useState(false);
    
        function handleToggle() {
          // Decouple the implementation of the parent state change from the child
          // Pass a function to change the state (async/batching reasons)
          setShow(show => !show);
        }
    
        return (
            <div>
                <A show={show} onToggle={handleToggle} />
                <B show={show} onToggle={handleToggle} /> 
            </div>
        )
    }
    
    const A = ({show, onToggle}) => (
      <div>
        <p>show: {show}</p>
        <button onClick={onToggle}>
          toggle
        </button>
      </div>
    )
    
    const B = ({show, onToggle}) => (
      <div>
        <p>show: {show}</p>
        <button onClick={onToggle}>
          toggle
        </button>
      </div>
    )
    

    So basically we don't care how the state in the parent is changed. We just know that when the button in the child component is clicked, we want to trigger that change. So all we really have to do is call the function passed down via props - we don't have to pass any params.

    The parent will then handle any clicks inside the handleToggle function, and you can change this implementation in the future without the child knowing anything. Perhaps you want to change to use mobx to handle state, or run some other code before finally changing the state. Since both are decoupled, you're all good! I've also adjusted setShow to use a function (benefits described here: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous).