torayeff
torayeff

Reputation: 9702

React js change child component's state from parent component

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?

Upvotes: 157

Views: 282780

Answers (8)

Matt
Matt

Reputation: 344

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>
  );
};

Upvotes: 0

chunky_azian
chunky_azian

Reputation: 17

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>)

Upvotes: -2

Idrees
Idrees

Reputation: 115

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}/>
  );
}

Upvotes: 4

Pranay kumar
Pranay kumar

Reputation: 2197

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}/>
    );
  }
}



Upvotes: 31

Juba Fourali
Juba Fourali

Reputation: 890

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.

Upvotes: 1

miguel savignano
miguel savignano

Reputation: 1159

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} />
  }
}

Upvotes: 35

Olivier Boiss&#233;
Olivier Boiss&#233;

Reputation: 18173

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}/>
      );
    }
}

Upvotes: 150

Jaison James
Jaison James

Reputation: 4552

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

Upvotes: 27

Related Questions