Reputation: 9702
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
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
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
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
Reputation: 2197
You can use the createRef
to change the state of the child component from the parent component. Here are all the steps.
React.createRef()
.ref={}
.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
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
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
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
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