How to update state of parent component n level above in React - javascript

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.

Related

React not calling function from another component?

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.

React component of grandparent callback doesnt re-render page after being call in grandchild

I am calling a handle method (to change state) in a <grandchild> component but it stop rendering after a couple of callback in the <grandparent> component.
I have tried to:
setting bind correctly with both this.bind in construct and arrow method.
making sure the call back is call everytime the prop.callback is call.
This is an example of what I'm trying to do with graphql server:
Grandparent Component
//Graphql constant for query
const ApolloConstant = gpl`
...etc
class Grandparent extends Component {
constructor(props) {
super(props);
this.state = { vars: 'query_string' }
}
handler = (args) => {
this.setState({vars: args})
}
render() {
return (
// For requerying graphql with search
<input onChange={() => this.setState(vars: e.target.value)} />
<Query variable={this.state.vars}>
...code -> some_data_arr
{<ChildComponent data={some_data_arr} handler={this.handler}/>}
</Query>
);
}
}
Child Component
//This component will take in an arr of obj and display a obj list
// if one of the obj is clicked then render a child component to display that single obj
class Child extends Component {
constructor(props) {
super(props);
this.state = {
singleData: null
}
}
render() {
return (
// Ternary operator here for conditional rendering
{
this.state.singleData
? <Grandchild data={this.state.singleData} handleParentData={this.props.handler} />
: this.display_data(this.props.data)
}
);
}
//Method to call to display objects
display_data = () => {
this.props.map() =>
<div onClick={this.setState({singleData: data})} > ...some code to display data <div/>
}
}
Grandchild Component
class Grandchild extends Component {
render() {
return (
{...do some code with object props here}
<Button onclick={(this.props.handleParentData(vars))} >Btn</Button>
);
}
}
When I test this, everything works for the first 3-4 render then no more re-rendering even though the callback is going through. I check to see if the method in <grandparent> is being call and it does but the state stop changing. Even after going to a new route (react router) and then coming back, I still cant change state with that callback.
<Button onclick={(this.props.handleParentData(vars))} >Btn</Button>
I think the problem is the function being called right into the onclick prop, you should probably have it wrapped in another function so it is only called when you actually trigger the onclick listener:
handleClick = () => {
this.props.handleParentData(vars)
}
render() {
return (
{...do some code with object props here}
<Button onclick={(this.handleClick)} >Btn</Button>
);
}

Changing component prop based on onBlur function in React

I have a component that I can change how it is rendered based on a prop (added a failed state, and based on whether it fails or not it turns red or stays the original colour), the logic for whether failed is true or false is in the parent component.
I want to change the failed state, but only onBlur (without changing the child component). Is there a way to pass in an onBlur function which applies changes to a child prop?
Ive tried a number of different things like:
Child component
<input
failed={failed}
onBlur={onBlur}
/>
Parent component:
this.props.failed = value;
}
and in the render function:
onBlur={() => this.handleBlur(newValue)}
but it didnt work for me.
Props are data that are passed from a parent to its children and are made available through this.props in the child component.
You maintain whatever prop your are passing to child component either in parent component's state or in redux/flux state (if you have global state management).
When failed is modified, a state change should be triggered on parent component, which in-turn will trigger a re-render inside child component.
For example:
In the following, we pass failed as a prop, and onFailureUpdate function as a callback trigger to child component from parent.
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
failed: false
}
}
onFailureUpdate = (value) => {
this.setState({
failed: value
});
}
render() {
return (<ChildComponent failed={this.state.failed} onFailureUpdate={this.onFailureUpdate} />)
}
}
In child component, on blur, we are using the function we passed as prop to modify state in parent, which in-turn will re-render child component.
class ChildComponent extends React.Component {
onBlur = (e) => {
this.props.onFailureUpdate(e.target.value);
}
render() {
return (
<input
value={this.props.failed}
onBlur={(e) => this.onBlur(e)}
/>
)
}
}
Other way:
Or, if there's no necessity for props or parent-child relationship, you can eliminate the need for parent container and go for state maintenance in child.
class RewrittenChildComponentWithState extends React.Component {
constructor() {
this.state = {
failed: false
};
}
onBlur = (e) => {
this.setState({
failed: e.target.value
});
}
render() {
return (
<input
value={this.state.failed}
onBlur={(e) => this.onBlur(e)}
/>
)
}
}
Hope this solves your confusion.

Accesing the state of child component

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

Pass value to parent component in React

essentially I have a form component for users to fill out. When they complete an action: onChange={onChange} it returns a value (child component);
function onChange(value) {
console.log("This is: ", value);
}
I want to pass the value to a state in the parent component (so that the from can be processed and form data sent). How can I do this? My constructor looks something like this (parent component);
constructor(props) {
super(props);
this.state = {
form: {
name: '',
age: '',
value: ''
}
};
}
Trying to learn react, please go easy as I'm unsure of how to do this. Would love any feedback! Thanks!
Please read the official documentation start guide carefully and patiently when you are a starter.
In react component, state is the internal state and props is the state passed from outside.
You could only modify the state by setState method.
As for the question you ask, you could define a callback function which call setState in parent component, then pass the callback function as props into the child component.
As Zhili said, you should define a function in your parent component that is passed as prop to the child component.
Here's a brief example:
const Child = ({ onSubmit }) => (
<form onSubmit={onSubmit}>
{ /* your <input/>'s here ... */}
</form>
);
class Parent extends React.Component {
state = {
value: null,
};
onChildSubmit = (value) => {
this.setState({
value,
});
}
render() {
return (
<div class="Something">
<Child onSubmit={} />
</div>
)
}
}

Categories