Updating child component state in react - reactjs

I'm having a lot of trouble updating the state of my child component, using props.
I have a parent component, called InputForm, which maintains a 2d array, which gets updated when the user fills out data in a form. This works correctly, however, I am trying to use this state variable to update the state of my child component, called Matrix. However, nothing I do seems to actually change the state of the Matrix component.
class InputForm extends Component {
constructor(props) {
super(props);
this.matrix = React.createRef();
this.state = {
equation: null,
integers: []
};
}
addIntegers = v => {
const newIntegers = this.state.integers.slice();
newIntegers.push(v);
this.setState({ integers: newIntegers });
this.matrix.current.changeElements(this.state.integers);
};
render() {
return (
<div>
<form onSubmit={this.mySubmitHandler}>
<input
type="text"
name="equation"
onChange={this.handleInputChange}
/>
</form>
<Matrix ref={this.matrix} values={this.state.integers} />
</div>
);
}
class Matrix extends Component {
state = {
rows: 0,
cols: 0,
elements: [[]]
};
constructor(props) {
super(props);
this.setState({ elements: this.props.value });
}
changeElements = props => {
this.setState({ elements: this.props.values });
console.log(this.elements);
};

In the parent component you are passing values as props
<Matrix ref={this.matrix} values={this.state.integers} />
while in the Matrix you are accessing:
constructor(props) {
super(props);
this.setState({ elements: this.props.value });
}
where this.props.value is not there, you should access the this.props.values

Because, this.setState (...) is asynchronous function. if you want to call this.matrix.current.changeElements(this.state.integers); function after updated the parent state, set the second argument of this.setState (...) to the callback function.
This is the fixed code
class InputForm extends Component {
...
addIntegers = v => {
const newIntegers = this.state.integers.slice();
newIntegers.push(v);
this.setState({ integers: newIntegers }, () => { // use callback
this.matrix.current.changeElements(this.state.integers);
});
};
...
class Matrix extends Component {
constructor(props) {
super(props);
this.state = {
rows: 0,
cols: 0,
elements: this.props.value || [[]] // try like this.
};
}
changeElements = props => {
// this.setState({ elements: this.props.values }); // wrong
this.setState({ elements: props.values }, () => {
console.log(this.state.elements);
}); // maybe like this
};
This is a simple example.
class App extends React.Component {
constructor(props) {
super(props);
this.child = React.createRef();
this.state = {
value: "aaa",
}
};
updateChild = () => {
this.setState({value: "bbb"}, () => {
this.child.current.changeElements(this.state.value);
})
};
render() {
return (
<div>
<button onClick = {this.updateChild} > Click here </button>
<Child ref={this.child} values={this.state.value} />
</div>
);
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.values,
};
}
changeElements = value => {
this.setState({ value });
console.log(value);
};
render() {
console.log(this.state.value)
return (
<div>{this.state.value}</div>
);
}
}
ReactDOM.render( < App / > , document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

Related

Passing an object as props and receiving it

I have a component Data and its child component BarChart.
Data component looks as following:
export default class Data extends Component {
constructor(props) {
super(props);
this.state = {
data: {
labels: [],
datasets: [{
label: "",
data: [],
backgroundColor: ''
}]
}
}
}
componentWillMount() {
this.geData();
}
geData = () => {
let labelsData = someContent;
let datasets = otherContentl;
this.setState({data: {...this.state.data, labels: labelsData, datasets: datasets}}, ()=>{console.log(this.state.data)});
}
render(){
return (
<BarChart data={this.state.data} />
);
}
}
When I check the result of console.log(this.state.data) in getData function, it prints out the correct data.
However, when I receive the props in BarChart component, I only receive datasets key filled with the correct data, but labels key is an empty array.
export default class BarChart extends Component {
constructor(props) {
super(props);
console.log(props);
}
componentWillMount() {
this.setState({chartData: this.props.data});
}
render() {
return (
<div className="barChart">
<Bar
data={this.state.chartData}
/>
</div>
);
}
}
Why does that happen? How can it be fixed?
What I had in BarChart component is:
constructor(props) {
super(props);
this.state = {
chartData: {}
}
}
componentWillMount() {
this.setState({chartData: this.props.data});
}
render() {
return (
<div className="barChart">
<Bar
data={this.state.chartData}
/>
</div>
);
}
What I changed is receiving the props immediately and using it, instead of receiving it in state or componentWillMount:
export default class BarChart extends Component {
constructor(props) {
super(props);
this.state = {}
}
componentWillMount() { }
componentDidMount() { }
render() {
return (
<div className="barChart">
<Bar
data={this.props.data}
/>
</div>
)
}
}
So, whenever the props is changed, the component will re-render.

Call child component function from parent

How do I call a child component function from the parent component? I've tried using refs but I can't get it to work. I get errors like, Cannot read property 'handleFilterByClass' of undefined.
Path: Parent Component
export default class StudentPage extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
newStudentUserCreated() {
console.log('newStudentUserCreated1');
this.refs.studentTable.handleTableUpdate();
}
render() {
return (
<div>
<StudentTable
studentUserProfiles={this.props.studentUserProfiles}
ref={this.studentTable}
/>
</div>
);
}
}
Path: StudentTable
export default class StudentTable extends React.Component {
constructor(props) {
super(props);
this.state = {
studentUserProfiles: props.studentUserProfiles,
};
this.handleTableUpdate = this.handleTableUpdate.bind(this);
}
handleTableUpdate = () => (event) => {
// Do stuff
}
render() {
return (
<div>
// stuff
</div>
);
}
}
UPDATE
Path StudentContainer
export default StudentContainer = withTracker(() => {
const addStudentContainerHandle = Meteor.subscribe('companyAdmin.addStudentContainer.userProfiles');
const loadingaddStudentContainerHandle = !addStudentContainerHandle.ready();
const studentUserProfiles = UserProfiles.find({ student: { $exists: true } }, { sort: { lastName: 1, firstName: 1 } }).fetch();
const studentUserProfilesExist = !loadingaddStudentContainerHandle && !!studentUserProfiles;
return {
studentUserProfiles: studentUserProfilesExist ? studentUserProfiles : [],
};
})(StudentPage);
My design here is: component (Child 1) creates a new studentProfile. Parent component is notified ... which then tells component (Child 2) to run a function to update the state of the table data.
I'm paraphrasing the OP's comment here but it seems the basic idea is for a child component to update a sibling child.
One solution is to use refs.
In this solution we have the Parent pass a function to ChildOne via props. When ChildOne calls this function the Parent then via a ref calls ChildTwo's updateTable function.
Docs: https://reactjs.org/docs/refs-and-the-dom.html
Demo (open console to view result): https://codesandbox.io/s/9102103xjo
class Parent extends React.Component {
constructor(props) {
super(props);
this.childTwo = React.createRef();
}
newUserCreated = () => {
this.childTwo.current.updateTable();
};
render() {
return (
<div className="App">
<ChildOne newUserCreated={this.newUserCreated} />
<ChildTwo ref={this.childTwo} />
</div>
);
}
}
class ChildOne extends React.Component {
handleSubmit = () => {
this.props.newUserCreated();
};
render() {
return <button onClick={this.handleSubmit}>Submit</button>;
}
}
class ChildTwo extends React.Component {
updateTable() {
console.log("Update Table");
}
render() {
return <div />;
}
}

React properties not bubbling down

I have a react component(parent) that has as state another react component(child)
The parent passes down is't state as props to the child.
But if I do setState on the passed down property, it does not update in the child.How do I make such that a change in state is reflected in the child?
See code:
class Child extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
{this.props.x}
</div>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {x: 1, intervalID: 0, currentScreen: <Child x={0} />}
}
componentDidMount() {
let self = this
let intervalID = setInterval(function() {
self.setState({x: self.state.x+1})
}, 1000)
self.setState({intervalID: intervalID, currentScreen: <Child x={self.state.x} />})
}
render() {
return (
<div>
{this.state.currentScreen}
</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('app'))
Below code is working.
import React from 'react';
import ReactDOM from 'react-dom';
class Child extends React.Component {
render() {
return (
<div>
{this.props.x}
</div>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {x: 1, intervalID: 0, currentScreen: <Child x={0} />}
}
componentDidMount() {
let intervalID = setInterval(() => {
const x = this.state.x + 1;
this.setState({
x: x,
currentScreen: <Child x={x} />
});
}, 1000)
this.setState({intervalID: intervalID, currentScreen: <Child x={this.state.x} />})
}
render() {
return (
<div>
{this.state.currentScreen}
</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('root'))
Your child component is not updating because for the lifecycle of parent component componentDidMount is only called once when it is being mounted.
If you need to update your state on regular interval you can do something like :
import React, { Component } from 'react';
import { render } from 'react-dom';
class Child extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
{this.props.x}
</div>
)
}
}
class Parent extends Component {
constructor(props) {
super(props)
this.state = { x: 1, intervalID: 0 }
}
componentDidMount() {
let self = this
let intervalID = setInterval(function () {
self.setState({ x: self.state.x + 1 })
}, 1000)
self.setState({ intervalID: intervalID })
}
render() {
return (
<div>
<Child x={this.state.x}/>
</div>
)
}
}
render(<Parent />, document.getElementById('root'))
for your case, just so when setState is done, it will call render again and it will pass the latest value of x to the child component.
You can check out live working example on stackblitz
It is a bad practise to maintain JSX in the state. Move all your JSX into the render() and use state variables to manage the state as shown below (For brevity only the Parent component code is shown).
Further instead of doing let self=this use the arrow function for clarity.
Note that you need to use the updater function when setting the state if your new state depends on the previous state. This is because React does batch updates for state. More information can be found in the official documentation.
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = { x: 1 }
}
componentDidMount() {
setInterval(() => {
this.setState((prevState, props) => {
return { x: prevState.x + 1 };
});
},3000)
}
render() {
return (
<div>
<Child x={this.state.x} />
</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('root'))
Above function will update the value of x every 3 seconds. Below is a working example
https://codesandbox.io/s/2677zoo4p

React - how to update state in parent component onclick in child - what am I doing wrong?

I have a parent and child component and simply take the input and on click I want to sort of update the parent. My onChange function works, but onClick I get the error message: Cannot read property 'name' of undefined - meaning that I never actually updated the parent. I cant figure out what am I doing wrong because as I understand, Im am passing the function correctly. Anyone knows? Thanks!
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {name: 'Frarthur'};
this.changeName = this.changeName.bind(this);
}
changeName(newName) {
this.setState({
name: newName
});
}
handleInput() {
console.log("helloooooo", this.state.name)
}
render() {
return (
<div>
<Child name={this.state.name} onChange={this.changeName} onClick={this.handleInput}/>
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleInput2 = this.handleInput2.bind(this);
}
handleChange(e) {
const name = e.target.value;
this.props.onChange(name);
}
handleInput2() {
this.props.onClick()
}
render() {
return (
<div>
<h1>
Hey my name is {this.props.name}!
</h1>
<input onChange={this.handleChange}/>
<input type="submit" onClick={this.handleInput2}/>
</div>
)
}
}
You could bind them in your constructor, like you did w/ changeName:
constructor(props) {
super(props);
this.state = {name: 'Frarthur'};
this.changeName = this.changeName.bind(this);
this.handleInput = this.handleInput.bind(this);
}
Alternatively, you could apply the proposed ES7 functionality (still considered "experimental" but extremely likely to be supported) property initializer by using fat arrow function:
handleInput = () => {
console.log("helloooooo", this.state.name)
};
Note, this may require you to update your project's configuration to support stage-0 preset for babel.
You haven't bind the
handleInput()
method in the Parent component. Here is the corrected Parent component
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { name: 'Frarthur' };
this.changeName = this.changeName.bind(this);
this.handleInput = this.handleInput.bind(this);
}
changeName(newName) {
this.setState({
name: newName
});
}
handleInput() {
console.log("helloooooo", this.state.name)
}
render() {
return (
<div>
<Child name={this.state.name} onChange={this.changeName} onClick={this.handleInput} />
</div>
)
}};

Incrementing state value by one using React

In React I am trying to make a button increment a value stored in state.
However using the code below function my value is set undefined or NaN when using handleClick.
class QuestionList extends React.Component {
constructor(props) {
super(props);
this.state = {value: 0};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick = (prevState) => {
this.setState({value: prevState.value + 1});
console.log(this.state.value)
}
Can you tell me why this is happening? it should be correct according to the docs here:
https://facebook.github.io/react/docs/state-and-lifecycle.html
Because you are using the handleClick function incorrectly. Here:
handleClick = (prevState) => { .... }
prevState will be an event object passed to handleClick function, you need to use prevState with setState, like this:
handleClick = () => {
this.setState(prevState => {
return {count: prevState.count + 1}
})
}
Another issue is, setState is async so console.log(this.state.value) will not print the updated state value, you need to use callback function with setState.
Check more details about async behaviour of setState and how to check updated value.
Check the working solution:
class App extends React.Component {
constructor(props){
super(props);
this.state={ count: 1}
}
onclick(type){
this.setState(prevState => {
return {count: type == 'add' ? prevState.count + 1: prevState.count - 1}
});
}
render() {
return (
<div>
Count: {this.state.count}
<br/>
<div style={{marginTop: '100px'}}/>
<input type='button' onClick={this.onclick.bind(this, 'add')} value='Inc'/>
<input type='button' onClick={this.onclick.bind(this, 'sub')} value='Dec'/>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='container'></div>
set state is async so you wont see the value update when the console.log happens. You should have the state value printed out on the UI so you can see whats happening. To fix the console log try this.
class QuestionList extends React.Component {
constructor(props) {
super(props);
this.state = {value: 0};
}
handleClick = (prevState) => {
this.setState({value: prevState.value + 1}, () => {
console.log(this.state.value)
});
}
NOTE: when you define an inline lambda (arrow function) for a react class this is bound correctly so you dont need to bind it in the constructor.
also you can change the way you pass the previous number if its just a state increment like this
handleClick = () => {
this.setState({value: this.state.value + 1}, () => {
console.log(this.state.value)
});
}
Hello there, try these codes to increment your value
class Counter extends React.Component{
constructor(props){
super(props);
this.addOne = this.addOne.bind(this);
this.state = {
count : 0
}
}
addOne() { // addOne as HandleClick
this.setState((preState) => {
return {
count : preState.count + 1
};
});
}
render() {
return (
<div>
<h1>Count : {this.state.count}</h1>
<button onClick={this.addOne}>+1</button>
</div>
);
}
}
ReactDOM.render(<Counter />, document.getElementById('YOUR-ID'));
class SkuVariantList extends React.Component {
constructor(props) {
super(props)
this.state = {
clicks: 0
};
this.clickHandler = this.clickHandler.bind(this)
}
componentDidMount() {
this.refs.myComponentDiv.addEventListener('click', this.clickHandler);
}
componentWillUnmount() {
//this.refs.myComponentDiv.removeEventListener('click', this.clickHandler);
}
clickHandler() {
var clk = this.state.clicks
this.setState({
clicks: clk + 1
});
}
render() {
let children = this.props.children;
return (
<div className="my-component" ref="myComponentDiv">
<h2>My Component ({this.state.clicks} clicks})</h2>
<h3>{this.props.headerText}</h3>
{children}
</div>
);
}
}
Try this out
class QuestionList extends React.component {
constructor(props){
super(props)
this.state = {
value : 0
}
}
handleClick(){
this.setState({
value : this.state.value + 1
})
}
render(){
return( <button type="button" onClick={this.handleClick.bind(this)}> {this.state.value} </button> )
}
}
Note that when you set a state, it triggers the render function, which will reflect the current state. Try it out in the browser!
import React from 'react'
class App extends React.Component{
constructor(){
super()
this.state = {
count: 0
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(){
this.setState(prevState => {
return {
count: prevState.count + 1
}
})
}
render(){
return(
<div style = {{display: 'flex', fontSize: 30, flexDirection: 'column', alignItems:'center'}}>
<h1>{this.state.count}</h1>
<button onClick = {this.handleClick}>Change</button>
</div>
)
}
}
export default App
This is the shortest code for that. First, initialize the state, then perform a method to increment.
state = {
counter: 0
}
increaseHandler = () => {
let counter = this.state.counter
counter += 1
this.setState({counter: counter})
}
You can do it this way also where we do both increment and decrement operation with same function making it more modular and redable
class CounterApp extends React.Component{
constructor(){
super();
//here count is initially assigned with 0
this.state ={
count:0
}
}
//when we click Increment or Decrement +1 or -1 is passed to step and the value gets changed it gets updated to the view
increment = (step) =>{
this.setState({
count:this.state.count + step
})
}
render(){
const { count } = this.state;//same as const count = this.state.count;
return(
<div>
<div className="counter-app">
<h2 className="value">{count}</h2>
<button onClick={() => this.increment(+1)}>Increment</button>
<button onClick={() => this.increment(-1)}>Decrement</button>
</div>
</div>
)
}
}

Resources