State is not getting updated in componentWillMount - reactjs

class ViewExchange extends React.Component{
state={list:[],refresh:false}
componentWillMount(props){
if(_.isEmpty(Cookies.get())){
this.props.history.push("/signup")
}
else{
console.log("is present")
let platform = Cookies.get('platform')
console.log(platform)
axios.post('http://localhost:3001/user/viewexchange',{platform})
.then(res=>{
console.log(res.data)
this.setState({list:res.data})})
console.log(this.state.list)
}
}
render(){
return (
<div>
<button onClick={()=>this.setState({refresh:true})}>refresh</button>
{console.log(this.state.refresh)}
</div>
);
}
}
export default withRouter(ViewExchange);

setState state update operations are async, so it will take a bit of time to update the state. But your log executes before it updates. Instead you can make use of passing function as a second param in the setState:
this.setState({list:res.data}, () => console.log(this.state.list))
another way is:
this.setState(state => {
state.list = res.data
}, () => console.log(this.state.list))

Whilst Jai's answer is correct, I also don't believe you are initialising your state correctly.
As you're using a class component you must initialise the state within the class's constructor.
constructor(props) {
super(props);
this.state = {
list: [],
refreshing: false,
};
}

Related

Why are two network calls being made, when fetch in setState?

When I use fetch in setState the function makes two network requests, but I expect one request.
Why is this happening and how to prevent it?
import React from 'react';
class TestFetch extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(() => {
fetch('http://example.com/', {
mode: 'no-cors'
})
.then(data => {
console.log(data)
});
});
}
render() {
return (
<button onClick={this.handleClick}> Test </button>
)
}
}
export default TestFetch
Another version with setState in the fetch. Now I have one network call, but two values in my state after one click:
import React from 'react';
class TestFetch extends React.Component {
constructor(props) {
super(props);
this.state = {
'newItems': []
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
fetch('http://example.com/', {
mode: 'no-cors'
})
.then(data => {
this.setState((state) => {
state.newItems.push("value")
})
console.log(this.state)
});
}
render() {
return (
<button onClick={this.handleClick}> Test </button>
)
}
}
export default TestFetch
Ok, basically it has this effect in this example as well:
import React from 'react';
class TestFetch extends React.Component {
constructor(props) {
super(props);
this.state = {
'newItems': []
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => {
state.newItems.push("value")
})
console.log(this.state);
}
render() {
return (
<button onClick={this.handleClick}> Test </button>
)
}
}
export default TestFetch
Don't do api call in setState.. take state variable and store api response data in it and use state variable when ever it's required.
import React from 'react';
class TestFetch extends React.Component {
constructor(props) {
super(props);
this.state = {appData: null};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
fetch('http://example.com/', {
mode: 'no-cors'
})
.then(data => {
console.log(data)
this.setState(() => {appData: data});
});
}
render() {
return (
<button onClick={this.handleClick}> Test </button>
)
}
}
export default TestFetch
Why is this happening...
My guess would be you are rendering your app into a React.StrictMode component. See Detecting unintentional side-effects
Strict mode can’t automatically detect side effects for you, but it
can help you spot them by making them a little more deterministic.
This is done by intentionally double-invoking the following functions:
Class component constructor, render, and shouldComponentUpdate methods
Class component static getDerivedStateFromProps method
Function component bodies
State updater functions (the first argument to setState)
Functions passed to useState, useMemo, or useReducer
In other words, the setState is called twice by React to help you find unintentional side-effects, like the double fetching.
...and how to prevent it?
Just don't do side-effects in the setState callback function. You likely meant to do the fetch and in the Promise chain update state.
handleClick() {
fetch('http://example.com/', {
mode: 'no-cors'
})
.then(data => {
console.log(data);
this.setState( ......); // <-- update state from response data
});
}
Update
Another version with setState in the fetch. Now I have one network
call, but two values in my state after one click:
In your updated code you are mutating the state object. Array.prototype.push updates the array by adding the new element to the end of the array and returns the new length of the array.
Array.prototype.push
this.setState(state => {
state.newItems.push("value") // <-- mutates the state object
})
I believe you see 2 new items added for the same reason as above. When updating arrays in state you need to return a new array reference.
You can use Array.prototype.concat to add the new value and return a new array:
this.setState(prevState => {
newItems: prevState.newItems.concat("value"),
});
Another common pattern is to shallow copy the previous state array into a new array and append the new value:
this.setState(prevState => {
newItems: [...prevState.newItems, "value"],
});
Additionally, once you sort out your state updates, the console log of the state won't work because React state updates are asynchronously processed. Log the updated state from the componentDidUpdate lifecycle method.
componentDidUpdate(prevProps, prevState) {
if (prevState !== this.state) {
console.log(this.state);
}
}

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.

Why this event handling function is being called twice and updates the state twice in react?

import React, { Component } from 'react'
class TestState extends Component{
constructor(props){
super(props)
this.state = {
count:1
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(){
console.log(this.state)
this.setState (state=>{
return state.count++
})
}
render(){
return <div>
<button onClick={this.handleClick} >Click</button>
{this.state.count}
</div>
}
}
export default TestState
In the above code, when I click the button the counter increases by double value every time I click it .. e.g. On clicking the button the count will increase by 1, 3, 5, 7?
But state.count should only increase once because of the ++ operator.
you should update the state like this
handleClick(){
console.log(this.state)
this.setState (state=>{
return { count: state.count + 1}
})
}
because you return a object that you wish update not a integer
The first answer is a solution, but the lifecycle method can be executed another way:
In my experience, ES6 fashion lends a DRY approach to updating state. Use of anonymous functions and implicit returns allow omission of the return keyword even though they aren't required if you don't rely on the value for another calculation ( Not recommended due to #setState's async nature ).
handleClick() {
console.log( this.state )
this.setState( state => ({
counter: state.count + 1
}))
}
For more on the matter see: https://reactjs.org/docs/state-and-lifecycle.html
Mutating state directly, as you do in your example, can lead to odd behavior and is never advisable. Here is a quote from the React docs:
NEVER mutate this.state directly, as calling setState() afterwards may
replace the mutation you made. Treat this.state as if it were
immutable.
By doing this state.count++ you throw React for a loop and as such see unexpected results.
I would rewrite your code like this: (https://codesandbox.io/s/red-lake-yk9gy?file=/src/App.js):
import React, { Component } from "react";
class TestState extends Component {
constructor(props) {
super(props);
this.state = { count: 1 };
}
handleClick = count => {
count++;
this.setState({count});
};
render() {
const { count } = this.state;
return (
<div>
<button onClick={() => this.handleClick(count)}>Click</button>
{count}
</div>
);
}
}
export default TestState;

getting error: Cannot read property state of undefined

import React, { Component } from "react";
import FormUpdate from "../components/formUpdate";
import { fetchClothingItem, updateClothingItem } from "../actions/crud";
export default class Update extends Component {
constructor(props) {
super(props);
this.state = {
updateClothingItem: {}
};
}
componentWillMount() {
fetchClothingItem(this.props.match.params.postId)
.then(data => {
this.setState(state => {
state.updateClothingItem = data;
return state;
});
console.log("data", data);
//HERE IT IS RETURNING EXPECTED DATA
console.log("this.state.updateClothingItem",this.state.updateClothingItem)
})
.catch(err => {
console.error("err", err);
});
}
handleSubmit(data) {
//HERE IT IS THROWING:
> "TypeError: Cannot read property 'state' of undefined"
console.log("this.state.updateClothingItem", this.state.updateClothingItem);
updateClothingItem(this.state.updateClothingItem.id, data); this.props.router.push("/update");
}
render() {
return (
<div>
<FormUpdate
//onSubmit={this.handleSubmit.bind(this)}
id={this.state.updateClothingItem.id}
name={this.state.updateClothingItem.name}
sleeveLength={this.state.updateClothingItem.sleeveLength}
fabricWeight={this.state.updateClothingItem.fabricWeight}
mood={this.state.updateClothingItem.body}
color={this.state.updateClothingItem.color}
/>
<button
type="submit"
onClick={this.handleSubmit}
className="addItemButton"
>
Button
</button>
</div>
);
}
}
There are a few things that are technically wrong in terms of React code implementation.
Firstly, With ES6 style of writing a class, any function that needs to access the Class properties need to be explicitly binded. In your case you need to bind the handleSubmit function using arrow function of or binding in constructor.
See this answer for more details: Why and when do we need to bind functions and eventHandlers in React?
Secondly: You have your async request set up in the componentWillMount function and in the success response of it, you are setting state. However using setState in componentWillMount is triggered after the component is rendered so you still need to have an undefined check. You should instead make use of componentDidMount lifecycle function for async requests.
Check this answer on whether to have AJAX request in componentDidMount or componentWillMount
Third: setState is asynchronous and hence logging the state values after the setState function won't result in the correct output being displayed. Use the setState callback instead.
See these answers for more details:
calling setState doesn't mutate state immediately
When to use React setState callback
Code:
export default class Update extends Component {
constructor(props) {
super(props);
this.state = {
updateClothingItem: {}
};
}
componentDidMount() {
fetchClothingItem(this.props.match.params.postId)
.then(data => {
this.setState(state => {
state.updateClothingItem = data;
return state;
});
console.log("data", data);
//HERE IT IS RETURNING EXPECTED DATA
console.log("this.state.updateClothingItem",this.state.updateClothingItem)
}) // this statement will not show you correct result since setState is async
.catch(err => {
console.error("err", err);
});
}
handleSubmit = (data) => { . // binding using arrow function here
console.log("this.state.updateClothingItem", this.state.updateClothingItem);
updateClothingItem(this.state.updateClothingItem.id, data); this.props.router.push("/update");
}
render() {
return (
<div>
<FormUpdate
//onSubmit={this.handleSubmit.bind(this)}
id={this.state.updateClothingItem.id}
name={this.state.updateClothingItem.name}
sleeveLength={this.state.updateClothingItem.sleeveLength}
fabricWeight={this.state.updateClothingItem.fabricWeight}
mood={this.state.updateClothingItem.body}
color={this.state.updateClothingItem.color}
/>
<button
type="submit"
onClick={this.handleSubmit}
className="addItemButton"
>
Button
</button>
</div>
);
}
}
You forgot to bind your handleSubmit function to the class. You can either use arrow function to define the function.
handleSubmit=(data) =>{
...
}
Or you can bind the function in your constructor.
constructor(props) {
super(props);
this.state = {
updateClothingItem: {}
};
this.handleSubmit= this.handleSubmit.bind(this,data);
}
there is no state in constructor yet
if you want to set state in constructor you can do it like this
class SomeComponent extends Component {
constructor(props){
super(props)
this.state = { someKey: someValue }
}
}
or even like this
class SomeComponent extends Component {
state = { someKey: someValue }
}
but in this case babel should be properly configured

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