Reactjs state not updating - javascript

constructor(props) {
super(props);
this.state = {
active: false,
showSideBar: false,
className: ""
}
}
componentDidMount() {
if (this.props.overlay) {
this.setState({
className: "wrapper_overlay"
});
alert(this.state.className);
}
else if (this.props.SideBarWithIcon) {
this.setState({
className: "wrapper_clopsed"
});
}
}
I am updating my state with the help of the props but the component is getting props but state is not updating

setState is asynchronous. Just alert in a callback to the method instead.
if (this.props.overlay) {
this.setState(
{ className: "wrapper_overlay" },
() => alert(this.state.className);
);
}
Note: you can, and should, also use shouldComponentUpdate to check for when a setState call completes

Since setstate is async in nature so you maynot see updated state in alert.
You can use that in callback which will be called once the setstate is done. like this
componentDidMount() {
if (this.props.overlay) {
this.setState({
className: "wrapper_overlay"
}, ()=> {alert(this.state.className);});
}

State updates may be asynchronous
React may batch multiple setState() calls into a single update for
performance.
Because this.props and this.state may be updated asynchronously, you
should not rely on their values for calculating the next state.
// Wrong
this.setState({
className: "wrapper_overlay"
});
To fix it, use a second form of setState() that accepts a function
rather than an object.
// Correct
this.setState((state, props) => ({
className: "wrapper_overlay"
}));
or just
this.setState(() => ({
className: "wrapper_overlay"
}));

In React.js, running program thread does not wait for setting state and continues its execution, until and unless operation defined in setState() callback.
Your state is getting set but as your alert() is after setstate i.e. not in callback of setState() that's why its getting previous value because at the time state is setting new value, thread is not waiting but executing the next instruction which is alert().
if (this.props.overlay) {
this.setState({ className: "wrapper_overlay" }, () => alert(this.state.className););
}
Hope this works for you.

Just use the callback method to update the state, as
setState() might work asynchronously.
this.setState(() => {
className: "wrapper_clopsed"
});

Related

React - How to add multiple values for a the state

I am trying to add multiple values from an array to the state, but for some reasons, only the last one is added into the state
here is the code:
this.state = { languageList: {} };
const languages = ['English', 'Spanish', 'Russian']
languages.map(language =>
this.setState({
languageList: {
...this.state.languageList,
[language]: true,
},
}),
);
Can anyone help?
Expected output:
languageList: { Engish: true, Spanish: true, Russian: true }
You can call setState only once rather than calling it into a loop.
If you do it into a loop, only the last setState will be applied because the previous ones haven't been executed yet.
this.setState({
languageList: {
...this.state.languageList,
...Object.fromEntries(languages.map(language => [language, true]))
}
}),
If you really want to call setState into a loop (I don't recommend it because it cause a lot of state updates), you must use the updater function to do the update from the previous state
languages.map(language =>
this.setState(prevState => ({
languageList: {
...prevState.languageList,
[language]: true,
},
})),
);
Since you're updating state based on previous values, you need to use the updater form:
this.setState(prevState => ({
languageList: {
...prevState.languageList,
[language]: true,
},
})),
This is because setState is async, and your loop will run to completion before the state is actually updated. Meaning this.state.languageList will be the same empty object for each iteration.

this.state inside setState ReactJS

So I have some confusion regarding the async nature of setState in ReactJS.
As per React docs, you shouldn't use this.state inside setState(). But if I have a counter as a state and i want to update it on click like this:
class App extends React.Component {
state = { counter: 0 }
onClick = () => {
this.setState({counter: this.state.counter + 1})
}
render() {
return (
<div>
<div>{this.state.counter}</div>
<p onClick={this.onClick}>Click me</p>
</div>
)
}
}
This works as expected. So why is this code wrong?
UPDATE: I know that setState is async, and it accepts a callback which has previous state as an argument, but I am not sure why I should use it here? I want to refer to the old state inside setState, so why should I use the callback function in this case? Whenever this.setState() is executed, this.state inside it will always refer to the old state, and its value will be changed to the new state only after setState has finished executing, not while it is executing.
You have access to prevState from within your setState call:
this.setState((prevState) => ({
counter: prevState.counter +1
}))
That will allow you to safely increment the current state value.
The React documentation summarises why you cannot rely on this.state to be accurate during update: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
setState accepts a function as a parameter with previous state as argument.
Refacto as follows to avoid competition problems:
onClick = () => {
this.setState(prevState => ({counter: prevState.counter + 1}))
}

Can't call setState on an unmounted component (Async still running)

I have an asynchronous function like so:
componentDidMount() {
someAsyncFunction().then((data) => {
this.setState({ something: data });
});
}
If go back to the previous screen, I will get the following error:
Warning: Can't call setState (or forceUpdate) on an unmounted component.
Is there something I can do to cancel this setState() if I go back to a previous screen while the async Is still running?
You can use this workaround:
componentDidMount() {
this._ismounted = true;
someAsyncFunction().then((data) => {
if (this._ismounted) {
this.setState({ something: data });
}
});
}
componentWillUnmount() {
this._ismounted = false;
}
This way the sesState will be called only if the component is mounted.
But this (as suggested in the comments) is an antiPattern and is to be used only when there is no another way to cancel the asyncFunction instead of waiting for it to be solved and then make the check.
First, you should not use isMounted() to wrap your code in an if-Statement.
(https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html)
In your case I think you have several choices: You could fire an action in your asynchronous function which sets the redux state instead of the components state.
Or if you really need it, you could set a flag in your componentDidMount and set it on false in componentWillUnmount.
constructor(props){
super(props);
this.state={
mounted: true
}
}
componentDidMount() {
this.setState({mounted: true});
someAsyncFunction().then((data) => {
if(this.state.mounted) this.setState({ something: data });
});
}
componentWillUnmount() {
this.setState({mounted: false});
}

console is not showing the updated state value after setState [duplicate]

Ok, i'll try and make this quick because it SHOULD be an easy fix...
I've read a bunch of similar questions, and the answer seems to be quite obvious. Nothing I would ever have to look up in the first place! But... I am having an error that I cannot fathom how to fix or why its happening.
As follows:
class NightlifeTypes extends Component {
constructor(props) {
super(props);
this.state = {
barClubLounge: false,
seeTheTown: true,
eventsEntertainment: true,
familyFriendlyOnly: false
}
this.handleOnChange = this.handleOnChange.bind(this);
}
handleOnChange = (event) => {
if(event.target.className == "barClubLounge") {
this.setState({barClubLounge: event.target.checked});
console.log(event.target.checked)
console.log(this.state.barClubLounge)
}
}
render() {
return (
<input className="barClubLounge" type='checkbox' onChange={this.handleOnChange} checked={this.state.barClubLounge}/>
)
}
More code surrounds this but this is where my problem lies. Should work, right?
I've also tried this:
handleOnChange = (event) => {
if(event.target.className == "barClubLounge") {
this.setState({barClubLounge: !this.state.barClubLounge});
console.log(event.target.checked)
console.log(this.state.barClubLounge)
}
So I have those two console.log()'s, both should be the same. I'm literally setting the state to be the same as the event.target.checked in the line above it!
But it always returns the opposite of what it should.
Same goes for when I use !this.state.barClubLounge; If it starts false, on my first click it remains false, even though whether the checkbox is checked or not is based off of the state!!
It's a crazy paradox and I have no idea whats going on, please help!
Reason is setState is asynchronous, you can't expect the updated state value just after the setState, if you want to check the value use a callback method. Pass a method as callback that will be get executed after the setState complete its task.
Why setState is asynchronous ?
This is because setState alters the state and causes re rendering. This can be an expensive operation and making it synchronous might leave the browser unresponsive.
Thus the setState calls are asynchronous as well as batched for better UI experience and performance.
From Doc:
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
Using callback method with setState:
To check the updated state value just after the setState, use a callback method like this:
setState({ key: value }, () => {
console.log('updated state value', this.state.key)
})
Check this:
class NightlifeTypes extends React.Component {
constructor(props) {
super(props);
this.state = {
barClubLounge: false,
seeTheTown: true,
eventsEntertainment: true,
familyFriendlyOnly: false
}
}
handleOnChange = (event) => { // Arrow function binds `this`
let value = event.target.checked;
if(event.target.className == "barClubLounge") {
this.setState({ barClubLounge: value}, () => { //here
console.log(value);
console.log(this.state.barClubLounge);
//both will print same value
});
}
}
render() {
return (
<input className="barClubLounge" type='checkbox' onChange={this.handleOnChange} checked={this.state.barClubLounge}/>
)
}
}
ReactDOM.render(<NightlifeTypes/>, document.getElementById('app'))
<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='app'/>
Since setState is a async function. That means after calling setState state variable does not immediately change. So if you want to perform other actions immediately after changing the state you should use callback method of setstate inside your setState update function.
handleOnChange = (event) => {
let inputState = event.target.checked;
if(event.target.className == "barClubLounge") {
this.setState({ barClubLounge: inputState}, () => { //here
console.log(this.state.barClubLounge);
//here you can call other functions which use this state
variable //
});
}
}
This is by-design due to performance considerations. setState in React is a function guaranteed to re-render Component, which is a costly CPU process. As such, its designers wanted to optimize by gathering multiple rendering actions into one, hence setState is asynchronous.

computed style in setState of react doesn't work

I have nested form objects but I try to setState using one handler, somehow doesn't work
constructor() {
super()
this.state = {
form: {
name: '',
location: ''
}
}
}
handleFormInput = (event) => {
this.setState({
[this.state.form[event.target.name]]: event.target.value
})
setTimeout(() => {
console.log(this.state.form)
},50)
}
event.target.name can be name and location.
You cannot directly access and modify a dynamic state within the setState function, you would rather get a copy of the state object and modify it. Also as you may already know that setState is async and hence you have a setTimeout function which is not necessary since setState provides you a callback function which is executed when state has changed.
handleFormInput = (event) => {
var form = {...this.state.form}
form[event.target.name] = event.target.value;
this.setState({
form
}, () => {this.state.form})
}
I think this code is not working properly:
this.setState({
[this.state.form[event.target.name]]: event.target.value
})
alternative is :
handleFormInput = (event) => {
//New code
let obj = {}
obj[this.state.form[event.target.name]] = event.target.value
this.setState(obj,() => { console.log(this.state.form) })
}
to view updated state, use callback function param of this.setState
setState is asynchronous, your state will properly update somehow after the call. Then render will be called again. Try checking your new state in render or in componentWillUpdate : https://facebook.github.io/react/docs/react-component.html#componentwillupdate
You should never look for a change in your state right after changing your state

Categories