State not updating when receiving new props (ReactJS) - reactjs

I'm new to React. I'm stuck on this, would really appreciate some help!
A parent component will pass an array into this child component. When I console.log(this.props.repairs) it shows me an array of 4. I am trying to update this.state.sortedDataList whenever the array of repairs is passed in. The console.log(this.state) is still showing sortedDataList as an empty array.
What am I doing wrong? Thanks so much, appreciate any help.
class Repairs extends React.Component {
constructor(props) {
super(props);
this.state = {
sortedDataList: []
};
}
componentWillReceiveProps(nextProps) {
if(this.props != nextProps) {
this.setState({
sortedDataList: this.props.repairs
});
}
}
render() {
console.log(this.props);
console.log(this.state);
return (
<div></div>
);
}
}

Never mind, found my silly mistake! If anyone else gets stuck on this in the future...
componentWillReceiveProps(nextProps) {
if(this.props != nextProps) {
this.setState({
sortedDataList: nextProps.repairs
});
}
}

componentWillReceiveProps isn't called on the first render. That is the reason that you don't see any update in the state
From the React Docs:
"Invoked when a component is receiving new props. This method is not called for the initial render."
If you want to make the change only first time you can make use of componentWillMount lifecycle function and update the state. On subsequent changed you componentWillReceiveProps will be called.
class Repairs extends React.Component {
constructor(props) {
super(props);
this.state = {
sortedDataList: []
};
}
componentWillMount() {
this.setState({
sortedDataList: this.props.repairs
}, () => console.log(this.state.sortedDataList));
}
componentWillReceiveProps(nextProps) {
if(this.props != nextProps) {
this.setState({
sortedDataList: nextProps.repairs
});
}
}
render() {
console.log("r",this.props);
console.log("r",this.state);
return (
<div></div>
);
}
}
class App extends React.Component {
render() {
var arr = ["1", "2", "3"];
return (
<div >
<Repairs repairs={arr}/>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="app"></div>

In your constructor
this.state = {
sortedDataList: []
};
You initially set state to an empty array, so on first render it'll be empty. Then, whenever the props are changed, it'll get updated via componentWillReceiveProps().
As Shubham said, componentWillReceiveProps() isn't called on the first render. If you want the state to reflect the props right from the first render, you'd have to put it in the constructor like so:
this.state = {
sortedDataList: this.props.repair
};

Related

Where to set state when I need that state in render?

I am getting this error below:
react_devtools_backend.js:2430 Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
From the error, I know I am getting it because I am setting state in the render.
But I am not sure where to set the state because I need that state element, developerTitle further down inside the render method.
Where can I put it if not in render?
Thanks!
Here is my code:
export default class Game extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
developerTitle: ''
}
}
render() {
const { indieDeveloperId } = this.props;
this.setState({ developerTitle: this.getDeveloperTitle(game.indieDeveloperId) });
<div>
<h3>{this.state.developerTitle}</h3>
...
...
</div>
}
//by-indie-developer/{indieDeveloperId
async getDeveloperTitle(indieDeveloperId) {
const r = await axios.get(`/api/developer/by-indie-developer/${indieDeveloperId}`);
const developerTitle = r.data;
this.setState({
...this.state, ...{
developerTitle: developerTitle
}
});
}
}
You can't set a state in render(). But you can set a state when the component is loaded using the componentDidMount() function.
Add a function with that name like this to your component:
componentDidMount() {
this.setState({ developerTitle: this.getDeveloperTitle(game.indieDeveloperId) });
}
You dont have to call the function. The state will automatically be set.

How can the state in react-js turns back to their original value

I'm exploring state in ReactJS and encountered this problem. What I want to happen is when the state === 0 it will go back to its original value. Here's my code:
export default class Test extends React.Component {
constructor (props) {
super(props);
this.state ={
stateValue: 5
}
}
And inside componentDidUpdate() is I decrement the value of state by 1 and have a condition in setState but I don't know what to put inside setState in order for the state to come back to its original value again.
if (stateValue === 0) {
this.setState({ })
}
and inside return is
<div>
<h1>{stateValue}</h1>
</div>
The result is it counts from 5 down to 0 but when it gets 0. It doesn't change back to its original value.
What I want to happen is when it turns to 0 it will go back to its value again which is 5 then continue to count down again. How can I do this? Thanks in advance
You can store another property in your set state.
export default class Test extends React.Component {
const originalValue = 5;
constructor (props) {
super(props);
this.state ={
stateValue: originalValue
}
}
}
And then while updating you can assign that property
if (stateValue === 0) {
this.setState({ stateValue: originalValue })
}
You can impliment this kind of logic with initial state with class property:
class Example extends Component {
defaultState = {
stateValue: 5,
};
constructor(props) {
super(props);
this.state = this.defaultState;
}
componentDidUpdate(_prevProps, _prevState) {
if (this.state.stateValue === 0) {
this.setState(this.defaultState);
}
}
render() {
const { stateValue } = this.state;
return (
<div>
<h1>{stateValue}</h1>
</div>
);
}
}

React: how to use setState and render component when prop changes

This app is supposed to filter words by a specific input. I want to call a function with setState() when rendering a component and technically it's working but there is warning in the console.
Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
I guess that this is because I'm calling the function in the render function which I shouldn't, but what should I do instead?
class UsersList extends React.Component {
constructor(props) {
super(props);
this.state = {
allUsers: ["Michał", "Ania", "Kasia", "Tomek", "Hubert", "Jan", "Martyna", "Rafał", "Bartłomiej"],
filteredUsers: [],
input: null
}
}
filter() {
if (this.state.input !== this.props.inputValue) {
const filtered = this.state.allUsers.filter(user => user.toLowerCase().includes(this.props.inputValue));
this.setState({
filteredUsers: filtered.map(user => <li key={user}>{user}</li>),
input: this.props.inputValue
})
}
return this.state.filteredUsers;
}
render() {
this.filter()
return (
<ul>
{this.state.filteredUsers}
</ul>
)
}
}
class App extends React.Component {
constructor() {
super();
this.state = {input: ""};
this.handleInput = this.handleInput.bind(this);
}
handleInput(e) {
this.setState({input: e.target.value})
}
render() {
return (
<div>
<input onChange={this.handleInput} type="search"/>
<UsersList inputValue={this.state.input} />
</div>
);
}
}
The issue here is caused by changes being made to your component's state during rendering.
You should avoid setting component state directly during a components render() function (this is happening when you call filter() during your component's render() function).
Instead, consider updating the state of your component only as needed (ie when the inputValue prop changes). The recommended way to update state when prop values change is via the getDerivedStateFromProps() component life cycle hook.
Here's an example of how you could make use of this hook for your component:
class UsersList extends React.Component {
constructor(props) {
super(props);
this.state = {
allUsers: ["Michał", "Ania", "Kasia", "Tomek",
"Hubert", "Jan", "Martyna", "Rafał",
"Bartłomiej"],
filteredUsers: [],
input: null
}
}
/* Add this life cycle hook, it replaces filter(). Props are updated/incoming
props, state is current state of component instance */
static getDerivedStateFromProps(props, state) {
// The condition for prop changes that trigger an update
if(state.input !== props.inputValue) {
const filtered = state.allUsers.filter(user => user.toLowerCase().includes(props.inputValue));
/* Return the new state object seeing props triggered an update */
return {
allUsers: state.allUsers
filteredUsers: filtered.map(user => <li key={user}>{user}</li>),
input: props.inputValue
}
}
/* No update needed */
return null;
}
render() {
return (<ul>{this.state.filteredUsers}</ul>)
}
}
Hope this helps
The error is coming up as it could create an endless loop inside the component. As render method is executed whenever the state is updated and your function this.filter is doing a state update. Now as the state updates, your render method triggers the function again.
Best way to do that would be in lifecycle methods or maintain the uses in the App and make UserList a dumb component by always passing the list of filtered users for it to display.

Call a function on application startup in react

I'm trying to call a function from application startup. The function reads data from JSON via dataVar (set elsewhere) and tries to load it into {items} for further consumption:
const dataVar = JSONStuff;
class Global extends Component {
constructor(props) {
super(props);
this.state = {
query: '',
items: []
}
this.init();
}
// componentDidMount() {
// This doesn't work either!
// this.init();
// }
init() {
let { items } = dataVar;
this.setState({items});
}
render() {
return (
<div className="Global">
<Gallery items={this.state.items}/>
</div>
)
}
}
Then in Gallery.js:
import React, { Component } from 'react';
class Gallery extends Component {
render() {
return (
<div>
<h3>gallery:</h3>
{
this.props.items.map((item, index) => {
let {title} = item.name;
return (
<div key={index}>{title}</div>
)
})
}
</div>
)
}
}
export default Gallery;
Not sure why Global can't call a function inside of itself. I've tried with and without "this." I either get error to where the app won't complile or I get:
"Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op."
First of all, it's a warning, you probably better not call setState in componentDidMount.
My suggestion 1: assign value to state in constructor
constructor(props) {
super(props);
this.state = {
query: '',
items: dataVar.items,
};
}
Suggestion 2:
Do inside the componentWillReceiveProps
componentWillReceiveProps(nextProps) {
const { dataVar: items } = nextProps; // pass dataVar as props
this.setState({
items,
});
}
Plus try to debug your props and pay attention on your console for errors.

React child component can't get props.object

My parent component is like this:
export default class MobileCompo extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
datasets: {}
};
this.get_data = this.get_data.bind(this);
}
componentWillMount() {
this.get_data();
}
async get_data() {
const ret = post_api_and_return_data();
const content={};
ret.result.gsm.forEach((val, index) => {
content[val.city].push()
});
this.setState({data: ret.result.gsm, datasets: content});
}
render() {
console.log(this.state)
// I can see the value of `datasets` object
return (
<div>
<TableElement dict={d} content={this.state.data} />
<BubbleGraph maindata={this.state.datasets} labels="something"/>
</div>
)
}
}
child component:
export default class BubbleGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
finalData: {datasets: []}
};
console.log(this.props);
// here I can't get this.props.maindata,it's always null,but I can get labels.It's confusing me!
}
componentWillMount() {
sortDict(this.props.maindata).forEach((val, index) => {
let tmpModel = {
label: '',
data: null
};
this.state.finalData.datasets.push(tmpModel)
});
}
render() {
return (
<div>
<h2>{this.props.labels}</h2>
<Bubble data={this.state.finalData}/>
</div>
);
}
}
I tried many times,but still don't work,I thought the reason is about await/async,but TableElement works well,also BubbleGraph can get labels.
I also tried to give a constant to datasets but the child component still can't get it.And I used this:
this.setState({ datasets: a});
BubbleGraph works.So I can't set two states at async method?
It is weird,am I missing something?
Any help would be great appreciate!
Add componentWillReceiveProps inside child componenet, and check do you get data.
componentWillReceiveProps(newProps)
{
console.log(newProps.maindata)
}
If yes, the reason is constructor methos is called only one time. On next setState on parent component,componentWillReceiveProps () method of child component receives new props. This method is not called on initial render.
Few Changes in Child component:
*As per DOC, Never mutate state variable directly by this.state.a='' or this.state.a.push(), always use setState to update the state values.
*use componentwillrecieveprops it will get called on whenever any change happen to props values, so you can avoid the asyn also, whenever you do the changes in state of parent component all the child component will get the updates values.
Use this child component:
export default class BubbleGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
finalData: {datasets: []}
};
}
componentWillReceiveProps(newData) {
let data = sortDict(newData.maindata).map((val, index) => {
return {
label: '',
data: null
};
});
let finalData = JSON.parse(JSON.stringify(this.state.finalData));
finalData.datasets = finalData.datasets.concat(data);
this.setState({finalData});
}
render() {
return (
<div>
<h2>{this.props.labels}</h2>
<Bubble data={this.state.finalData}/>
</div>
);
}
}

Resources