149

I have two components: Parent Component from which I want to change child component's state:

class ParentComponent extends Component {
  toggleChildMenu() {
    ?????????
  }
  render() {
    return (
      <div>
        <button onClick={toggleChildMenu.bind(this)}>
          Toggle Menu from Parent
        </button>
        <ChildComponent />
      </div>
    );
  }
}

And Child Component:

class ChildComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false;
    }
  }

  toggleMenu() {
    this.setState({
      open: !this.state.open
    });
  }

  render() {
    return (
      <Drawer open={this.state.open}/>
    );
  }
}

I need to either change Child Component's open state from Parent Component, or call Child Component's toggleMenu() from Parent Component when Button in Parent Component is clicked?

torayeff
  • 9,296
  • 19
  • 69
  • 103
  • Maybe you can hold a child reference in parent, and change child's state explicitly,See this [doc](https://reactjs.org/docs/refs-and-the-dom.html) – lin Mar 14 '18 at 01:43

8 Answers8

150

The state should be managed in the parent component. You can transfer the open value to the child component by adding a property.

class ParentComponent extends Component {
   constructor(props) {
      super(props);
      this.state = {
        open: false
      };

      this.toggleChildMenu = this.toggleChildMenu.bind(this);
   }

   toggleChildMenu() {
      this.setState(state => ({
        open: !state.open
      }));
   }

   render() {
      return (
         <div>
           <button onClick={this.toggleChildMenu}>
              Toggle Menu from Parent
           </button>
           <ChildComponent open={this.state.open} />
         </div>
       );
    }
}

class ChildComponent extends Component {
    render() {
      return (
         <Drawer open={this.props.open}/>
      );
    }
}
pwolaq
  • 6,343
  • 19
  • 45
Olivier Boissé
  • 15,834
  • 6
  • 38
  • 56
  • Can this be used to control a css property like 'display'? as in, if my prop 'open' contains either 'none' or 'inline-block', will the css display prop get updated? – deusofnull Aug 29 '17 at 21:53
  • I ended up using the react-classname package instead and used the 'display' prop as a boolean for whether or not to add a 'hidden' class to the element. It was easier and my sr dev recommended it over my method. thanks for the response though! – deusofnull Aug 30 '17 at 22:20
  • I don't know the react-classname package, one clean solution would be like this `className={this.props.open ? 'show' : 'hidden'}` – Olivier Boissé Aug 31 '17 at 06:51
  • 3
    Yeah that is essentially what the react-classnames package does, but it also allows you to always apply a set of classnames, and conditionally apply others. Like this: `classNames({ foo: true, bar: this.props.open });` // => 'foo' when this.props.open = false and 'foo bar' when this.props.open = true. – deusofnull Aug 31 '17 at 19:55
  • 3
    How can we change the open state in child component? – Diptesh Atha Feb 02 '18 at 08:19
  • 1
    you can add a property `toggle` to the ChildComponent `` and call `this.props.toggle()` in the child component – Olivier Boissé Feb 02 '18 at 10:49
  • @OlivierBoissé How can we call this.props.toggle() in the child component? – Jaison James Feb 12 '18 at 07:29
  • 1
    I don't understand, you can call it wherever you want in the child component as soon as you specified this property when declaring the `ChildComponent` -> `` – Olivier Boissé Feb 12 '18 at 08:26
  • 1
    @OlivierBoissé what about the case where you have multiple child elements ? do you have to keep the state of each separately in the parent component? – Yasir Jan Nov 09 '18 at 12:24
  • @YasirJan you can declare an array into your state, each element in the array will match one child, you can use a `map` function in the `render` method to display the children – Olivier Boissé Nov 09 '18 at 13:00
  • 19
    In this way, whole Parent component is being re-render by which we loose efficiency. Can you please tell any way in which only Child component will update its State or Props (means re-render) without re-render of Parent component. – Abid Ali Sep 25 '19 at 16:58
  • looking for Hooks solution – Faizan Mubasher Oct 05 '21 at 17:27
33

The parent component can manage child state passing a prop to child and the child convert this prop in state using componentWillReceiveProps.

class ParentComponent extends Component {
  state = { drawerOpen: false }
  toggleChildMenu = () => {
    this.setState({ drawerOpen: !this.state.drawerOpen })
  }
  render() {
    return (
      <div>
        <button onClick={this.toggleChildMenu}>Toggle Menu from Parent</button>
        <ChildComponent drawerOpen={this.state.drawerOpen} />
      </div>
    )
  }
}

class ChildComponent extends Component {
  constructor(props) {
    super(props)
    this.state = {
      open: false
    }
  }

  componentWillReceiveProps(props) {
    this.setState({ open: props.drawerOpen })
  }

  toggleMenu() {
    this.setState({
      open: !this.state.open
    })
  }

  render() {
    return <Drawer open={this.state.open} />
  }
}
miguel savignano
  • 1,109
  • 13
  • 9
  • 2
    in react 16 use getDerivedStateFromProps – Fadi Abo Msalam Nov 12 '18 at 14:46
  • 1
    @FadiAboMsalam I'm using react version 16.7.0 with @Types/react version 16.7.18. At least on the TypeScript side there doesn't seem to be `getDerivedStateFromProps()`. However, Miguel's answer suggesting to use `componentWillReceiveProps(props)` is available and worked like a charm in my env. – Manfred Dec 28 '18 at 02:48
  • 3
    In this case, how would the toggleMenu() state change inside the child component would reach the parent? Imagine I close the drawer, how would the parent component know it's been closed? – norman123123 Aug 20 '20 at 17:39
30

You can use the createRef to change the state of the child component from the parent component. Here are all the steps.

  1. Create a method to change the state in the child component.
  2. Create a reference for the child component in parent component using React.createRef().
  3. Attach reference with the child component using ref={}.
  4. Call the child component method using this.yor-reference.current.method.

Parent component


class ParentComponent extends Component {
constructor()
{
this.changeChild=React.createRef()
}
  render() {
    return (
      <div>
        <button onClick={this.changeChild.current.toggleMenu()}>
          Toggle Menu from Parent
        </button>
        <ChildComponent ref={this.changeChild} />
      </div>
    );
  }
}

Child Component


class ChildComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false;
    }
  }

  toggleMenu=() => {
    this.setState({
      open: !this.state.open
    });
  }

  render() {
    return (
      <Drawer open={this.state.open}/>
    );
  }
}



Evandro Pomatti
  • 13,341
  • 16
  • 97
  • 165
Pranay kumar
  • 1,983
  • 4
  • 22
  • 51
  • 6
    This is the best answer I have found, because it does not re-render the parent component. This is much faster and much better practice! – Big Sam May 14 '21 at 22:57
  • 2
    Yes this is actually the best answer too for my needs. That's very clean, we should upvote this answer. – Martin Gaucher Nov 12 '21 at 12:39
  • 1
    On parent, I had to use a handler method instead of directly calling the child. Then it worked. – Evandro Pomatti Nov 23 '21 at 00:01
  • this doesn't seem to work for functional components. what would the equivalent implementation look like for a functional child component? – codeprose-sam Jun 26 '23 at 19:25
27

Above answer is partially correct for me, but In my scenario, I want to set the value to a state, because I have used the value to show/toggle a modal. So I have used like below. Hope it will help someone.

class Child extends React.Component {
  state = {
    visible:false
  };

  handleCancel = (e) => {
      e.preventDefault();
      this.setState({ visible: false });
  };

  componentDidMount() {
    this.props.onRef(this)
  }

  componentWillUnmount() {
    this.props.onRef(undefined)
  }

  method() {
    this.setState({ visible: true });
  }

  render() {
    return (<Modal title="My title?" visible={this.state.visible} onCancel={this.handleCancel}>
      {"Content"}
    </Modal>)
  }
}

class Parent extends React.Component {
  onClick = () => {
    this.child.method() // do stuff
  }
  render() {
    return (
      <div>
        <Child onRef={ref => (this.child = ref)} />
        <button onClick={this.onClick}>Child.method()</button>
      </div>
    );
  }
}

Reference - https://github.com/kriasoft/react-starter-kit/issues/909#issuecomment-252969542

Jaison James
  • 4,414
  • 4
  • 42
  • 54
  • 2
    This is what I want, but I am wondering why not just use react refs? see[doc](https://reactjs.org/docs/refs-and-the-dom.html) – lin Mar 14 '18 at 01:37
  • What does the onRef prop do? – norman123123 Aug 20 '20 at 17:08
  • 3
    This is actually a discouraged pattern in react because it reverses information flow. If react has a single zen, it's _data down, actions up_. This is the concept that makes react apps so easy to reason about. Imperative handles can be useful but should be used sparingly and well-thought. They should not be the go-to solution for e.g. toggling visibility. – trixn Feb 24 '21 at 20:38
3

If you are using functional components. You can achieve this by:

const ParentComponent = () => {
   const [isOpen, setIsOpen] = useState(false)

   toggleChildMenu() {
      setIsOpen(prevValue => !prevValue)
   }

   return (
     <div>
       <button onClick={toggleChildMenu}>
         Toggle Menu from Parent
       </button>
       <Child open={isOpen} />
     </div>
   );
}



const Child = ({open}) => {
  return (
    <Drawer open={open}/>
  );
}
Idrees
  • 97
  • 1
  • 1
1

You can send a prop from the parent and use it in child component so you will base child's state changes on the sent prop changes and you can handle this by using getDerivedStateFromProps in the child component.

Juba Fourali
  • 740
  • 9
  • 10
0

Here's in TypeScript and using function components, hope it helps

/* Child.tsx */

import React, { useEffect } from "react";

type Props = {
   onRef: (callback: Function) => void;
 }

export const Child: React.FC<Props> ({onRef}) => {
  useEffect(() => {
    onRef(() => {
      console.log("Dude, see? It's working!");
    });
  }, [onRef]);
  
  return <h1 className={s.root}>Hello World!</h1>;
};

/* Parent.tsx */

import React, { useRef } from "react";
import Child from "./Child";

export const Parent = () => {
  const callbackRef = useRef<Function>();
  const handleOnRef = (callback: Function) => {
    callbackRef.current = callback;
  };
  
  return (
    <div>
      <Child onRef={handleOnRef} />
      <button
        onClick={() => {
          callbackRef?.current?.();
        }}
      >
        Child.method()
      </button>
    </div>
  );
};
Matt
  • 195
  • 7
-2

Here's another way that I tried yesterday

In your child script, define the method available to the parent and a regular component


    var ChildStateModificationFunc;
    const Child = ()=>{
    const [someState, setSomeState] = useState();

    //define the state that you want to modify
    ChildStateModificationFunc = (modVal)=>{
        setSomeState(modVal)
    }

    return (
    <div>
        {/* your child jsx here */}
    </div>
    }

    //export both the child and the method
    export default Child;
    export {ChildStateModificationFunc}


In your parent script, import both items

    import Child, {ChildStatteModificationFunc} from 'Child.js'

    const Parent = ()=>{

    var newVal = 'some parent val'  //let say you just fetch this from some web api
    //share the newVal with Child component
    ChildStatteModificationFunc(newVal)

    return(
    <div>
        <Child />
    </div>)