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?
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}/>
);
}
}
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} />
}
}
getDerivedStateFromProps()
. However, Miguel's answer suggesting to use componentWillReceiveProps(props)
is available and worked like a charm in my env.
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
You can use the createRef
to change the state of the child component from the parent component. Here are all the steps.
Create a method to change the state in the child component. Create a reference for the child component in parent component using React.createRef(). Attach reference with the child component using ref={}. 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}/>
);
}
}
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}/>
);
}
open
? If the child say has a button to close the drawer, how does this work out?
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.
Success story sharing
classNames({ foo: true, bar: this.props.open });
// => 'foo' when this.props.open = false and 'foo bar' when this.props.open = true.toggle
to the ChildComponent<ChildComponent open={this.state.open} toggle={this.toggleChildMenu.bind(this)} />
and callthis.props.toggle()
in the child componentChildComponent
-><ChildComponent toggle={this.toggleChildMenu.bind(this)} />