I want to access <Child2>
state from parent component.
<Parent>
<Child1>
<Child2>
</Child1>
</Parent>
How it can be achieved?
I want to access <Child2>
state from parent component.
<Parent>
<Child1>
<Child2>
</Child1>
</Parent>
How it can be achieved?
You shouldn't do this at all. In react information should always flow from top to bottom. The react way to do this is to lift state up to the parent and pass values down as props. In your child you have callbacks that get also passed as props that the child calls, when values change (e.g. through user interaction) so that the parent can update its state:
const Child = ({ value, onChange }) => (
<Fragment>
<p>{value || "no value"}</p>
<button onClick={() => onChange('changed')} >Set value</button>
</Fragment>
);
class Parent extends Component {
state = {
value: null
};
handleChange = value => this.setState({value})
render() {
return <Child value={this.state.value} onChange={this.handleChange} />;
}
}
Working example on codesandbox
This can be done infinitely deeply nested. In a bigger application where your state needs to be used in multiple components spread all over your component tree you may reach a point where this gets a PITA. The common way to overcome this is to use a global store like redux provides.
One way to avoid passing props down to multiple components is to use reacts context
api.
This allows us to acess state from the MyProvider
component 100 children down without manually passing the props down through 100 child components.
import React, { Component } from "react";
import { render } from "react-dom";
// first make a new context
const MyContext = React.createContext();
// Then Create a provider Component
class MyProvider extends Component {
state = {
name: "Junie",
age: 21
};
render() {
return (
<MyContext.Provider
value={{
state: this.state,
increment: () => this.setState({ age: this.state.age + 1 })
}}
>
{this.props.children}
</MyContext.Provider>
);
}
}
const App = () => {
return (
<MyProvider>
<div>
<p>Hello I'm the App </p>
<Child />
</div>
</MyProvider>
);
};
const Child = props => {
return (
<div>
<Child2 />
</div>
);
};
class Child2 extends Component {
render() {
return (
<div>
<p> Im Child 2 </p>
<MyContext.Consumer>
{context => (
<div>
{" "}
<p>
{" "}
Inside the context consumer: {context.state.name}{" "}
{context.state.age}
</p>{" "}
<button onClick={context.increment}>Update Age </button>
</div>
)}
</MyContext.Consumer>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Using context
is probably what you are looking for.
There is another implementation.
class Parent extends React.Component {
state={childstate:''}
getFromChild = (value) => {
this.setState({childstate:value})
}
render() {
return (
<div>
<Child1 getFromChild={this.getFromChild/>
{this.state.childstate && <div> {this.state.childstate} </div>}
</div>
)
}
}
const Child1 = (props) => <Child2 getFromChild={props.getFromChild}/>
class Child2 extends React.Component {
state={somevalue:10}
sendValue = (value) => this.props.getFromChild(value)
render() {
return (
<div>
<button onClick={() => this.sendValue(this.state.somevalue)} />
</div>
)
}
}
Quite simply we use a setter in the parent to get the state from the respective child.
We take a value as a parameter and set the state of the parents state to that value. I have named it childstate so its clear that whatever value we send to the parent came from the child.
getFromChild = (value) => this.setState({childstate:value})
Pass the function down as a prop from Child1
to Child2
<Child2 getFromChild={props.getFromChild}/>
In Child2
add onClick handler to send the value from the child to the Parent
<button onClick={() => this.sendValue(this.state.somevalue)} />
There is workaround possible by passing a ref from parent component to child component. Child component should be wrapped in forwardRef. Now hack is Whenever you update the child state, update that ref also at the same time.
With this done. You have access to all the child state as it is also saved on the ref.
const QEditor = (props, ref) => {
const [content, setContent] = useState(initialContent);
const handleChange = useCallback(
value => {
/* not all components passes ref */
if (ref?.current) {
/* setting the value on ref also */
ref.current.content = value;
}
setContent(value);
},
[ref]
);
... trim...
export default forwardRef(QEditor);
...
// in parent
const Parent = () => {
const newRef = useRef({});
cosole.log(newRef) // this will contain child's state.
return <QEditor ref={newRef} />
}