I have a parent component which contains a function, which when called needs to acces the childrenĀ“s component state. I dont want to move the whole state to the parent component because i want the children component to be independent. What is the cleanest and most recommended way to achieve this?
class ParentComponent extends Component {
render() {
return (
<div>
<ChildComponent/>
<SaveButton onClick={this.saveFunction}/>
</div>
)
}
saveFunction = () => {
//Here i need to acces the child Component state
}
}
My solution so far was that everytime something changed in child component i called a function which was passed from the parent Component. Like this:
class ChildrenComponent extends Component {
state = {
name: "David",
age: 19
}
render() {
return (
//inputs with the inputChange function
)
}
inputChange = (e) => {
//Update the state
//Then pass the state to the parent
this.props.passStateToParent(this.state)
}
}
I would recommend to look up some of the React patterns - especially Render Props, as it allows to expose the state and wanted methods of a component - what you want in this situation.
Best of luck!
You can make a function in parent component and pass it down to child component as prop. This function could return to parent component the state of your child component. See more here: https://reactjs.org/docs/faq-functions.html
You cannot directly access the state of the child component,this can be done by passing the state to methods of parent component which are passed as props to child component,the following example demonstrate how to do it .
class ParentComponent extends React.Component {
somefunc() {
//do your action
}
render() {
<ChildComponent parentfunc={this.somefunc}/>
}
}
class ChildComponent extends React.Component {
constructor(props){
super(props)
this.state = {somedate:'value'}
this.func = this.func.bind(this)
}
func() {
this.props.parentfunc(this.state)
}
render() {
<button onClick={this.func}>text</button>
}
}
Related
I'm trying to learn react and ran into a snag. I'm struggling to update the parent based on the child state. I've managed to pass the child state to the parent by binding the child's state to the same child's prop when invoked by the parent.
Parent.js
import React, { Component, setState } from 'react'
import './Parent.css'
import Child from './Child'
export class Parent extends Component {
constructor(props) {
super(props)
this.state = {
childState: false
}
}
checkState(newState){
console.log(`new state is ${newState}`)
}
render() {
return (
<div class={`parent ${this.state.childState ? 'parent-child-not-clicked' : 'parent-child-clicked'}`}>
<h1>{this.state.childState === true ? 'true' : 'false'}</h1>
{/* <Child changeState={(newState)=>{newState === true ? this.setState(prevState => ({childState: prevState.childState+1})):this.setState(prevState => ({childState: prevState.childState-1}))}}></Child> */}
<Child changeState={(newState) => {console.log(newState)}}></Child>
</div>
)
}
}
export default Parent
Child.js
import React, { Component } from 'react'
import "./Child.css"
export class Child extends Component {
constructor(props) {
super(props)
this.state = {
childState: false
}
this.updateState = this.updateState.bind(this)
}
updateState(){
this.setState({
childState: !this.state.childState
}, () => {return this.state.childState})
}
render() {
return (
<div className="child">
<h1>{`child state is ${this.state.childState}`}</h1>
<div onClick={() => this.props.changeState(this.updateState())}>Click</div>
</div>
)
}
}
export default Child
The console keeps rendering undefined, meaning newState doesn't contain the boolean value true / false. Would appreciate if anyone can point me in the right direction.
Thanks in adavance
this.updateState() doesn't return anything. So nothing is sent to this.props.changeState.
Probably the simplest approach is to remove this.props.changeState from the JSX markup and move it into updateState. Then within updateState define the new state object, update the component's state with it, and pass it to the prop function. Something like this:
updateState(){
const newState = {
childState: !this.state.childState
};
this.setState(newState);
this.props.changeState(newState);
}
Then in the JSX just call updateState (putting less logic inline in the JSX and more in the functions):
<div onClick={this.updateState}>Click</div>
As an aside, while the example shown is clearly a contrived one, tracking the same state in two different places is probably the wrong design. If the parent just needs updates, pass it just the updates that it needs. But if the parent is tracking the state, the child doesn't need to duplicate that effort. You can remove state from the child entirely and just pass it the values it needs, simplifying the whole thing.
I have a redux-store with objects of initial values. And this store will get updated at a few places within the child component.
I created a stateless functional component as parent
const Parent = () => {
const store = useSelector(state => state);
const getInitState = () => {
depends on store, it will return an object as initial state for child component
}
let initState = getInitState(); //it has to be let instead of const, it could be changed during useEffect
useEffect(() => {
some initialization on mount
}, [])
return ( // return is simplified here
<Child initState={iniState} />
)
}
export default Parent;
I have a class child component something like below
class Child extends Component {
state = {
componentState: this.props.initState
}
....
}
export default Child;
I can't modify the child component It's a very complex component with many sub components which I dont handle.
Now I need to access setState function of child component from parent. Or I need to change the state of child from parent, is there a way to do that?
Yes, I understand a new design should be consider since it's anti-pattern, but I am just wondering if I can do it under current setting.
Thank you all in advance.
==============================================================
Edit: For whoever runs into the same problem, functional component does not support constructor. So I have included a breif correction to the answer.
Define parent as below
import React, { useRef } from "react";
const Parent = () => {
const childRef = useRef(null);
return (
<Child ref={childRef} />
)
}
export default Parent;
Then you are able to use childRef.current to access all function from child component.
The best way is using a react Context , and set state in parent then the child consume state of parent (using react hooks would be so easy than class component)
but in your case as you mentiened (I wonder I can do it under current setting)
you can use react refs :
first put ref prop in your rendered component tag then use it in parent to execute function that's declared inside child
as below :
inside parent component :
const Parent = () => {
.
.
.
constructor() {
//create react ref for our component
this.childComponent = React.createRef();
}
callChildFunction() {
// here using refs you can access function in you child refenrenced component
this.childComponent.cuurent.doSomeUpdateStateStuff(newState);
}
return ( // return is simplified here
<Child ref={this.childComponen} initState={iniState} />
)
...
}
and your child :
class Child extends Component {
state = {
componentState: this.props.initState
}
doSomeUpdateStateStuff(state) {
// stuff updating state callled from parent
}
....
}
I believe I am trying to accomplish something simple but failing to do so.
React is not calling the 'alertFunc()' from the ChildComponent from another component like I hoped it would.
Here is the ChildComp:
class ChildComp extends React.Component {
constructor(props) {
super(props);
this.state = { };
this.input = React.createRef();
}
alertFunc = () => {
alert('This function is called from the Child Component');
};
handleChange = () => {
this.alertFunc();
};
render() {
return (
<ChildComp onChange={this.handleChange} />
);
}
}
Then I'm trying to call it from a parent compolike:
render(props){
return(
<button onClick={props.alertFunc()}>Next</button>
);
}
And the error I get is:
props.alertFunc is not a function
You can't call an instance function of a child component from a parent component like that, you don't have access to that function from the parent. If you wish both components to have access to it (parent and child) you should share it somehow between them, using context at a more upper-level if the hierarchy is deeply nested, or define it in the parent and pass it to the child via props or use redux. Or if doesn't depend on component state move it out of the component.
I am not really sure how to properly ask this question but I will explain what I'm trying to do here:
So I have this parent Component which creates a Component like so:
<CurrentTemperature cityName={this.state.cityName}></CurrentTemperature>
The CurrentTemperature Component looks like this:
import React, { Component } from "react";
import "../App.css";
export default class CurrentTemperature extends Component {
constructor(props) {
super(props);
this.state = {
temperature: 0,
cityName: this.props.cityName,
};
}
componentDidMount() {
//fetch the temperature from api here
}
render() {
return (
<div>
<div className="city-temperature">
{this.state.cityName} {this.state.temperature}
</div>
</div>
);
}
}
All I'm trying to do is read the city name from the parent, then fetch the current temperature from my API, and then display both of those in the Component. But if I try to console.log(this.props.cityName) from anywhere other than from inside the city-temperature div, I always get an empty string. What is going on here?
cityName is the state of the parent component. I guess the parent component would get the "cityName" asynchronously. right? If this is the case, You have to put the temperature in the parent component as its state. And you have to insert the API call in the parent component. CurrentTemperature component will behave like a pure function component.
const CurrentTemperature = ({temperature, cityName}) => {
return (
<div className="city-temperature">
{cityName} {temperature}
</div>
);
}
I guess this is not only the solution but also the best DX.
You can remove this in your constructor, and then use this.state.cityName
constructor(props) {
super(props);
this.state = {
temperature: 0,
cityName: props.cityName,
};
}
Check the code here
jsfiddle
I wish to update the value property of individual item from the Child component. But as props are immutable and don't trigger re-render the code doesn't work. One way I know to make this work is pass a function from GrandParent to Parent and then to Child and use it to update state of GrandpParent. This will trigger re-render in the Child component. But this also causes re-render of GrandParent, Parent and other siblings of Child component.
// comment
Is there a better way to do this, this doesn't seem optimal to me.
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleClick = this.handleClick.bind(this)
}
handleClick(e) {
this.props.handleIncrement(e.currentTarget.dataset.key)
}
render() {
return (
<div>
<span>{this.props.item.value}</span>
<button data-key={this.props.item.key} onClick={this.handleClick}>inc</button>
</div>
);
}
}
class Parent extends React.Component {
render() {
return (
<div>
{
this.props.list.map((item) => <Child item={item} handleIncrement={this.props.handleIncrement} />)
}
</div>
);
}
}
class GrandParent extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [
{
key: 'one',
value: 1
},
{
key: 'two',
value: 2
},
{
key: 'three',
value: 3
}
]
};
this.handleIncrement = this.handleIncrement.bind(this)
}
handleIncrement(key) {
this.setState({
list: this.state.list.map((l) => {
if (l.key === key) {
return {key: l.key, value: l.value + 1}
}
return l
})
})
}
render() {
return (<Parent list={this.state.list} handleIncrement={this.handleIncrement} />);
}
}
React.render(<GrandParent />, document.getElementById('container'));
You have to pass the handler from the Grand parent and call this handler whenever you wanted to increment. Read about coupling and cohesion for theoretical background.
React is based on the concept of unidirectional data flow. This means that your are passing data down to other components who receive it as props and render it, or passing it down to another sub component.
However, sometimes we want a child component to let a parent component that something happened. To solve this, we use callback. Callbacks are functions that we can pass as props to a child component, so he can use them we something happens. A classic example is to pass an onClick handler to a child component that has a button. Then, when the button is pushed the child component calls it like this:
this.props.onClick()
letting the parent know that the button was clicked. This will work for yor example too. Create a function in the GrandParent component that knows how to increment the value.
incrementValue = (idx) => {
// Copy the list to avoid mutating the state itself.
let newList = this.state.list.slice();
newList[idx].value += 1;
this.setState({list: newList});
}
Then pass this function as callback
<Parent onClick={this.incrementValue}/>
Then bind it to the button click like this:
<button onClick={this.props.onClick}>inc</button>
Read this to learn more about state and props in React.