Updating view from setState() not triggering rerender when expected - javascript

Answer:
I'm a bonehead. This is way late but don't want to leave a thread unanswered and especially since my initial answer was wrong. I needed to reference the new props I was getting, not this.props. As usual the answer was in the documentation. I updated my own answer below to reflect this.
Edit 1:
My first fiddle did not fully show my issue so I've updated it to demonstrate better. For some reason when I call my setState() I believe the first pass through it is undefined even though its given a value, yet on subsequent passes it works as expected. It seems like my initial setState() call is not triggering a rerender but all others are.
A bit different to the usual "setState not updating view" question as setState() IS updating my view and with the correct value. Just not when I expect it to. Basically I am triggering a setState() event should rerender my child component with new props which I believe should trigger the childs components componentWillReceiveProps lifecyle event, which will then call setState() within the child component and update its view. The issue is while it does update the view, it seems to do it a cycle behind when expected. In my MVP I call setState() at 2 seconds yet it updates the view at 4 seconds. I haven't been able to determine which part is the bad logic though.
Here is my jsFiddle MVP. Thanks for any suggestions.
Code:
class TaskBody extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentWillReceiveProps() {
this.setState({
activeTask: this.props.activeTask
});
}
render() {
return <div>
< h4 > {
this.state.activeTask ? this.state.activeTask : 'Task Title'
} < /h4>
< /div > ;
}
}
class Body extends React.Component {
constructor(props) {
super(props);
this.state = {
activeTask: '',
counter: 1
};
this.updateActive = this.updateActive.bind(this);
}
updateActive(task) {
this.setState({
activeTask: task
});
}
componentDidMount(){
var self = this;
setInterval(function(){
if(self.state.counter === 4){
clearInterval(self.clickInterval);
return clearInterval(self.countInterval);
}
self.setState({ counter: self.state.counter + 1 });
}, 1000);
// imagine this was the click event, it should rerender the view
// instantaneously at 2 seconds because I called setState() right?
// which is supposed to trigger a re-render and then TaskBody should
// receive new props triggering it to setState() on its activeTask,
// which should update the view?
self.clickInterval = setInterval(function(){
self.setState({ activeTask: 'took double the time it should' });
}, 2000);
}
render() {
return <div>
< TaskBody activeTask = {
this.state.activeTask
}/>
<div>{this.state.counter}</div>
</div>;
}
}
ReactDOM.render(<Body />, document.querySelector('#body'));

The
self.setState({ activeTask: 'took double the time it should' });
is never actually called.
Here's why:
Your logic is located within the componentDidMount lifecycle method. If you read the documentation of componentDidMount, you'll see that it clearly states:
Invoked once, only on the client (not on the server), immediately
after the initial rendering occurs.
So, when componentDidMount gets called in your app, you're first checking the counter and calling a setState there. That alone will trigger a new render. This means that your second code block within componentDidMount is going to have no effect because while you set the method, it's never going to get called anywhere.

In my old answer, I was using a setTimeout() as a hack to get the results I wanted. Basically, if I wrapped my setState in a timeout it would set the state with the new props, but if I did not it would still reference the old props. Fortunately, this is already handled in react as componentWillReceiveProps already receives a newProps argument by default.
This:
componentWillReceiveProps(){
var self = this;
setTimeout(function(){
self.setState({activeTask: self.props.activeTask});
},0);
}
becomes
componentWillReceiveProps(newProps){
var self = this;
self.setState({activeTask: newProps.activeTask});
}

Related

What is the advantage of using componentDidUpdate over the setState callback?

Why is using componentDidUpdate more recommended over the setState callback function (optional second argument) in React components (if synchronous setState behavior is desired)?
Since setState is asynchronous, I was thinking about using the setState callback function (2nd argument) to ensure that code is executed after state has been updated, similar to then() for promises. Especially if I need a re-render in between subsequent setState calls.
However, the official React Docs say "The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead."
And that's all they say about it there, so it seems a bit vague. I was wondering if there was a more specific reason it is recommended to not use it? If I could I would ask the React people themselves.
If I want multiple setState calls to be executed sequentially, the setState callback seems like a better choice over componentDidUpdate in terms of code organization - the callback code is defined right there with the setState call. If I use componentDidUpdate I have to check if the relevant state variable changed, and define the subsequent code there, which is less easy to track. Also, variables that were defined in the function containing the setState call would be out of scope unless I put them into state too.
The following example might show when it might be tricky to use componentDidUpdate:
private functionInComponent = () => {
let someVariableBeforeSetStateCall;
... // operations done on someVariableBeforeSetStateCall, etc.
this.setState(
{ firstVariable: firstValue, }, //firstVariable may or may not have been changed
() => {
let secondVariable = this.props.functionFromParentComponent();
secondVariable += someVariableBeforeSetStateCall;
this.setState({ secondVariable: secondValue });
}
);
}
vs
public componentDidUpdate(prevProps. prevState) {
if (prevState.firstVariableWasSet !== this.state.firstVariableWasSet) {
let secondVariable = this.props.functionFromParentComponent();
secondVariable += this.state.someVariableBeforeSetStateCall;
this.setState({
secondVariable: secondValue,
firstVariableWasSet: false,
});
}
}
private functionInComponent = () => {
let someVariableBeforeSetStateCall = this.state.someVariableBeforeSetStateCall;
... // operations done on someVariableBeforeSetStateCall, etc.
this.setState({
firstVariable: firstValue,
someVariableBeforeSetStateCall: someVariableBeforeSetStateCall,
firstVariableWasSet: true });
//firstVariable may or may not have been changed via input,
//now someVariableBeforeSetStateCall may or may not get updated at the same time
//as firstVariableWasSet or firstVariable due to async nature of setState
}
Also, apart from componentDidUpdate being generally recommended, in what cases would the setState callback be more appropriate to use?
Why is using componentDidUpdate more recommended over the setState callback function?
1. Consistent logic
When using the callback argument to setState(), you might have two separate calls to setState() in different places which both update the same state, and you'd have to remember to use the same callback in both places.
A common example is making a call to a third-party service whenever a piece of state changes:
private method1(value) {
this.setState({ value }, () => {
SomeAPI.gotNewValue(this.state.value);
});
}
private method2(newval) {
this.setState({ value }); // forgot callback?
}
This is probably a logic error, since presumably you'd want to call the service any time the value changes.
This is why componentDidUpdate() is recommended:
public componentDidUpdate(prevProps, prevState) {
if (this.state.value !== prevState.value) {
SomeAPI.gotNewValue(this.state.value);
}
}
private method1(value) {
this.setState({ value });
}
private method2(newval) {
this.setState({ value });
}
This way, the service is guaranteed to be called whenever the state updates.
Additionally, state could be updated from external code (e.g. Redux), and you won't have a chance to add a callback to those external updates.
2. Batched updates
The callback argument of setState() executes after the component is re-rendered. However, multiple calls to setState() are not guaranteed to cause multiple renders, due to batching.
Consider this component:
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = { value: 0 };
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate: ' + this.state.value);
}
onClick = () => {
this.setState(
{ value: 7 },
() => console.log('onClick: ' + this.state.value));
this.setState(
{ value: 42 },
() => console.log('onClick: ' + this.state.value));
}
render() {
return <button onClick={this.onClick}>{this.state.value}</button>;
}
}
We have two setState() calls in the onClick() handler, each simply prints the new state value to the console.
You might expect onClick() to print the value 7 and then 42. But actually, it prints 42 twice! This is because the two setState() calls are batched together, and only cause one render to occur.
Also, we have a componentDidUpdate() which also prints the new value. Since we only have one render occurring, it is only executed once, and prints the value 42.
If you want consistency with batched updates, it's usually far easier to use componentDidMount().
2.1. When does batching occur?
It doesn't matter.
Batching is an optimization, and therefore you should never rely either on batching occurring or it not occurring. Future versions of React may perform more or less batching in different scenarios.
But, if you must know, in the current version of React (16.8.x), batching occurs in asynchronous user event handlers (e.g. onclick) and sometimes lifecycle methods if React has full control over the execution. All other contexts never use batching.
See this answer for more info: https://stackoverflow.com/a/48610973/640397
3. When is it better to use the setState callback?
When external code needs to wait for the state to be updated, you should use the setState callback instead of componentDidUpdate, and wrap it in a promise.
For example, suppose we have a Child component which looks like this:
interface IProps {
onClick: () => Promise<void>;
}
class Child extends React.Component<IProps> {
private async click() {
await this.props.onClick();
console.log('Parent notified of click');
}
render() {
return <button onClick={this.click}>click me</button>;
}
}
And we have a Parent component which must update some state when the child is clicked:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { clicked: false };
}
private setClicked = (): Promise<void> => {
return new Promise((resolve) => this.setState({ clicked: true }, resolve));
}
render() {
return <Child onClick={this.setClicked} />;
}
}
In setClicked, we must create a Promise to return to the child, and the only way to do that is by passing a callback to setState.
It's not possible to create this Promise in componentDidUpdate, but even if it were, it wouldn't work properly due to batching.
Misc.
Since setState is asynchronous, I was thinking about using the setState callback function (2nd argument) to ensure that code is executed after state has been updated, similar to .then() for promises.
The callback for setState() doesn't quite work the same way as promises do, so it might be best to separate your knowledge.
Especially if I need a re-render in between subsequent setState calls.
Why would you ever need to re-render a component in between setState() calls?
The only reason I can imagine is if the parent component depends on some info from the child's DOM element, such as its width or height, and the parent sets some props on the child based on those values.
In your example, you call this.props.functionFromParentComponent(), which returns a value, which you then use to compute some state.
Firstly, derived state should be avoided, since memoization is a much better option. But even so, why not just have the parent pass the value directly down as a prop? Then you can at least compute the state value in getDerivedStateFromProps().
//firstVariable may or may not have been changed,
//now someVariableBeforeSetStateCall may or may not get updated at the same time
//as firstVariableWasSet or firstVariable due to async nature of setState
These comments don't make much sense to me. The asynchronous nature of setState() doesn't imply anything about state not getting properly updated. The code should work as intended.

React js state and lifecycle

componentDidMount() {
const user = auth.getCurrentUser();
this.setState({ user });
}
I have this code, I think this.setState({ user });takes a little time, if I want to do some checks immidetly like
<Route
path="/foo"
render={props =>
this.state.user ? (
<Bar {...props} />
) : (
<Redirect to="/login" />
)
}
/>
when refreshing the page the user at the beginning is always null. what is the proper solution? do I need to set state at constructor? or did I do something wrong?
My account got blocked by some down votes questions, the funny thing is I have to re-edit them, even though I already have the accepted answer.I do not understand what's the point to do this.I am so frustrated by this stackoverflow system.
Now, I basically can do nothing but keep editing my questions, and they have all been answered. This is ridiculous !!!
Yes, you should initialise your state in the constructor.
See the React docs example:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
when refreshing the page the user at the beginning is always null
#Shubham Khatri did explain it really well, in short, just because the render() function is called before the componentDidMount(), hence, the user is always null.
Take a look at this: React lifecycle methods diagram
And as you can see that, the proper place for setState should be the contructor() cuz it's called before the render().
However, for api calls why componentDidMount is the better place? why
we do not do all set up in constructor?
I know you're talking about this: https://reactjs.org/docs/faq-ajax.html . The document does say that: You should populate data with AJAX calls in the componentDidMount lifecycle method. This is so you can use setState to update your component when the data is retrieved.
However, in another place, they say:
You may call setState() immediately in componentDidMount(). It will
trigger an extra rendering, but it will happen before the browser
updates the screen. This guarantees that even though the render() will
be called twice in this case, the user won’t see the intermediate
state. Use this pattern with caution because it often causes
performance issues. In most cases, you should be able to assign the
initial state in the constructor() instead. It can, however, be
necessary for cases like modals and tooltips when you need to measure
a DOM node before rendering something that depends on its size or
position.
...and also for the case that you need to authenticate because this process depends on the value of the user ( as your design).
The problem in your code is that, componentDidMount is called after render and by the time your user details are fetched and stored in state, your component is ready to redirect to /login since user wan't available. To solve this issue, you need to fetch user details before the initial render and hence constructor is the ideal place to do it
constructor(props) {
super(props);
this.state = {
user: auth.getCurrentUser()
}
}
The state goes inside the constructor, but only if you need a constructor (e.g.:to initialize flags). If you don't need a constructor you can just initialize the state outside:
class MyComponent extends Component {
state = { myState = [] }
render() {
const { myState } = this.state
return (...)
}
}
You should use the constructor() to initiate state, componentDidMount() for call functions and componentWillReceiveProps() for setState.
constructor(props) {
super(props);
this.state = {
firstName: "",
lastName: ""
};
}
componentDidMount() {
this.props.userProfile();
}
componentWillReceiveProps(nextProps, prevProps) {
this.setState({
firstName: nextProps.user.firstName,
lastName: nextProps.user.lastName
});
}

Why sometimes render is called immediately after setState and sometimes not

here is my codes:
class TestState extends Component{
constructor(props){
super(props);
this.state={
text:"123"
}
this._hello = this._hello.bind(this)
}
_hello(){
console.log("guangy will set state...");
let num = Math.floor(Math.random()*100);
this.setState({text: ""+num });
console.log("guangy after set state...");
}
componentWillUpdate(){
console.log("guangy componentWillUpdate")
}
render(){
console.log("guangy render.....")
return(<View>
<Text>{this.state.text}</Text>
<Button title="click" onPress={
()=>{
this._hello();
}
}/>
<Button title="click1" onPress={
()=>{
setTimeout(()=>{
this._hello();
}, 10);
}
}/>
</View>);
}
}
when i clicked the first button, the log is:
guangy will set state...
guangy after set state...
guangy componentWillUpdate
guangy render.....
and logs when clicked the second button:
guangy will set state...
guangy componentWillUpdate
guangy render.....
guangy after set state...
i think the render function should be called asynchronous, and actually in most situation it is, like when i clicked the first button, but when i clicked the second button, the render function seems to be called synchronous, because the "after set state" log is printed after the "render" log. why does this happen?
As per the DOCS
Think of setState() as a request rather than an immediate command to
update the component. For better perceived performance, React may
delay it, and then update several components in a single pass. React
does not guarantee that the state changes are applied immediately.
setState() does not always immediately update the component. It may
batch or defer the update until later. This makes reading this.state
right after calling setState() a potential pitfall.
So one would think that setState is asynchronous. But if we look at some key words like:
React MAY delay it
setState() does not ALWAYS immediately update the component.
It MAY batch or defer the update until later.
So are we to think that setState is may or may not be an asynchronous operation?
Well, yep.
This is setState taken from the source code:
ReactComponent.prototype.setState = function(partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.'
);
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
Looks like a normal synchronous function, well actually setState as it self isn't asynchronous but the effect of its execution may be.
I've done some testing myself and i realized that react will only update the state Asynchronously is when react has control of the entire flow, where react can't control of the flow, which means the the execution context is outside of it, then react will update the state immediately.
What can take react's control then?
Things like setTimeout, setInterval ajax request and other webApi's.
Even an event handler that attached outside react will trigger such behavior.
In fact here is a little snippet to satisfy our experiment.
I have a React component App which has a state with a myValue key and a method named onClick.
This method logs the current state, then call setstate then logs the state again.
App renders 3 elements:
The current value of myValue.
A button that we attach onClick through the react API.
A button that we attach onClick through the addEventListener API
(out side of react mind you).
When we click on the first button when react has control over the flow, the state is updated asynchronously.
When we click the second button, react will update the state immediately.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
myVal: 0
}
}
componentDidMount() {
const el = document.getElementById('btn');
el.addEventListener('click', this.onClick);
}
onClick = () => {
console.log('Pre setState', this.state.myVal);
this.setState({ myVal: this.state.myVal + 1 });
console.log('Post setState', this.state.myVal);
console.log('-------------------');
}
render() {
const { myVal } = this.state;
return (
<div>
<div>{myVal}</div>
<button onClick={this.onClick}>Managed by react</button>
<button id='btn'>Not Managed by react</button>
</div>);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<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="root"></div>

react-native componentDidMount to fetch from server

constructor(props) {
super(props);
console.log("props");
this.state = {
userId : "12345",
};
}
componentDidMount() {
console.log("componentDidMount");
Actions.getProductDetails({userId:"123456"});
Actions.getProductDetails.completed.listen(this.gotProductDetails.bind(this));
Actions.cancelOrder.completed.listen(this.cancelOrderCompleted.bind(this));
}
gotProductDetails(data) {
console.log("gotProductDetails");
}
goBack(data) {
console.log("justgoback");
this.props.back();
}
cancelProduct() {
console.log("SDsadsadsad");
Actions.cancelOrder({
orderId:this.state.order.id,
canelMsg:this.state.selectedReason,
userId:this.state.userId
});
}
cancelOrderCompleted(data) {
console.log("cancelOrderCompleted");
this.goBack();
}
My issue is some functions are mounting twice whenever I change the
route and revisit this route again I would show you console.log here
This is for first time I come to this route:
props
cancelOrder.js:190 componentDidMount
cancelOrder.js:197 gotProductDetails
Now I will do cancelProduct call and log will be
SDsadsadsad
cancelOrder.js:221 cancelOrderCompleted
cancelOrder.js:210 justgoback
This is for second time i.e, I will go back from this route and revisit:
props
cancelOrder.js:190 componentDidMount
cancelOrder.js:197 gotProductDetails
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. Please check the code for the cancelOrder component.
cancelOrder.js:197 gotProductDetails
Now I will do cancelProduct call and log will be
SDsadsadsad
cancelOrder.js:221 cancelOrderCompleted
cancelOrder.js:210 justgoback
cancelOrder.js:221 cancelOrderCompleted
cancelOrder.js:210 justgoback
In the above log you can see that for the second time line number 197 221 210 executed twice with the error I was not able to solve
I'm using react navigator for route
I checked in release version also, but it is having same error it was told in one Github issue, but was not able to find now.
Every time you run this line
Actions.cancelOrder.completed.listen(this.cancelOrderCompleted.bind(this));
The listen method gets a new function instance every time it runs, so if this page was mounted twice in the app's lifecycle, the cancelOrderCompleted would run twice and one of them probably in an unmounted component which is bad.
Generally I would advise that your getProductDetails would return a Promise. If you don't want to do that, make sure you remove the listeners when your component is unmounted.
And be aware that cancelOrderCompleted.bind(this) creates a new delegate instance that you can't recreate when stopping the listener. Unless you keep it in a data member.
Edit:
Code example -
constructor(props) {
super(props);
console.log("props");
this.state={
userId : "12345",
}
this.getProductDetailsBound = this.gotProductDetails.bind(this);
this.cancelOrderCompletedBound = this.cancelOrderCompleted.bind(this);
}
componentDidMount() {
console.log("componentDidMount")
// Listen before you call getProductDetails, not after
Actions.getProductDetails.completed.listen(this.getProductDetailsBound);
Actions.cancelOrder.completed.listen(this.cancelOrderCompletedBound);
Actions.getProductDetails({userId:"123456"});
}
componentWillUnmount() {
Actions.getProductDetails.completed.stopListening(this.getProductDetailsBound);
Actions.cancelOrder.completed.stopListening(this.cancelOrderCompletedBound);
}

setState doesn't update the state immediately [duplicate]

This question already has answers here:
Why does calling react setState method not mutate the state immediately?
(9 answers)
The useState set method is not reflecting a change immediately
(15 answers)
Closed 8 months ago.
I would like to ask why my state is not changing when I do an onClick event. I've search a while ago that I need to bind the onClick function in constructor but still the state is not updating.
Here's my code:
import React from 'react';
import Grid from 'react-bootstrap/lib/Grid';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import BoardAddModal from 'components/board/BoardAddModal.jsx';
import style from 'styles/boarditem.css';
class BoardAdd extends React.Component {
constructor(props) {
super(props);
this.state = {
boardAddModalShow: false
};
this.openAddBoardModal = this.openAddBoardModal.bind(this);
}
openAddBoardModal() {
this.setState({ boardAddModalShow: true }); // set boardAddModalShow to true
/* After setting a new state it still returns a false value */
console.log(this.state.boardAddModalShow);
}
render() {
return (
<Col lg={3}>
<a href="javascript:;"
className={style.boardItemAdd}
onClick={this.openAddBoardModal}>
<div className={[style.boardItemContainer,
style.boardItemGray].join(' ')}>
Create New Board
</div>
</a>
</Col>
);
}
}
export default BoardAdd
Your state needs some time to mutate, and since console.log(this.state.boardAddModalShow) executes before the state mutates, you get the previous value as output. So you need to write the console in the callback to the setState function
openAddBoardModal() {
this.setState({ boardAddModalShow: true }, function () {
console.log(this.state.boardAddModalShow);
});
}
setState is asynchronous. It means you can’t call it on one line and assume the state has changed on the next.
According to React docs
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.
Why would they make setState async
This is because setState alters the state and causes rerendering. 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.
Fortunately setState() takes a callback. And this is where we get updated state.
Consider this example.
this.setState({ name: "myname" }, () => {
//callback
console.log(this.state.name) // myname
});
So When callback fires, this.state is the updated state.
You can get mutated/updated data in callback.
For anyone trying to do this with hooks, you need useEffect.
function App() {
const [x, setX] = useState(5)
const [y, setY] = useState(15)
console.log("Element is rendered:", x, y)
// setting y does not trigger the effect
// the second argument is an array of dependencies
useEffect(() => console.log("re-render because x changed:", x), [x])
function handleXClick() {
console.log("x before setting:", x)
setX(10)
console.log("x in *line* after setting:", x)
}
return <>
<div> x is {x}. </div>
<button onClick={handleXClick}> set x to 10</button>
<div> y is {y}. </div>
<button onClick={() => setY(20)}> set y to 20</button>
</>
}
Output:
Element is rendered: 5 15
re-render because x changed: 5
(press x button)
x before setting: 5
x in *line* after setting: 5
Element is rendered: 10 15
re-render because x changed: 10
(press y button)
Element is rendered: 10 20
Live version
Since setSatate is a asynchronous function so you need to console the state as a callback like this.
openAddBoardModal(){
this.setState({ boardAddModalShow: true }, () => {
console.log(this.state.boardAddModalShow)
});
}
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.
setState() will always lead to a re-render unless shouldComponentUpdate() returns false. If mutable objects are being used and conditional rendering logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.
The first argument is an updater function with the signature:
(state, props) => stateChange
state is a reference to the component state at the time the change is being applied. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from state and props. For instance, suppose we wanted to increment a value in state by props.step:
this.setState((state, props) => {
return {counter: state.counter + props.step};
});
Think of setState() as a request rather than an immediate command to
update the component. For better perceived performance, React may
delay it, and then update several components in a single pass. React
does not guarantee that the state changes are applied immediately.
Check this for more information.
In your case you have sent a request to update the state. It takes time for React to respond. If you try to immediately console.log the state, you will get the old value.
The above solutions don't work for useState hooks.
One can use the below code
setState((prevState) => {
console.log(boardAddModalShow)
// call functions
// fetch state using prevState and update
return { ...prevState, boardAddModalShow: true }
});
This callback is really messy. Just use async await instead:
async openAddBoardModal(){
await this.setState({ boardAddModalShow: true });
console.log(this.state.boardAddModalShow);
}
If you want to track the state is updating or not then the another way of doing the same thing is
_stateUpdated(){
console.log(this.state. boardAddModalShow);
}
openAddBoardModal(){
this.setState(
{boardAddModalShow: true},
this._stateUpdated.bind(this)
);
}
This way you can call the method "_stateUpdated" every time you try to update the state for debugging.
Although there are many good answers, if someone lands on this page searching for alternative to useState for implementing UI components like Navigation drawers which should be opened or closed based on user input, this answer would be helpful.
Though useState seems handy approach, the state is not set immediately and thus, your website or app looks laggy... And if your page is large enough, react is going to take long time to compute what all should be updated upon state change...
My suggestion is to use refs and directly manipulate the DOM when you want UI to change immediately in response to user action.
Using state for this purspose is really a bad idea in case of react.
setState() is asynchronous. The best way to verify if the state is updating would be in the componentDidUpdate() and not to put a console.log(this.state.boardAddModalShow) after this.setState({ boardAddModalShow: true }) .
according to React Docs
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately
According to React Docs
React does not guarantee that the state changes are applied immediately.
This makes reading this.state right after calling setState() a potential pitfall and can potentially return the existing value due to async nature .
Instead, use componentDidUpdate or a setState callback that is executed right after setState operation is successful.Generally we recommend using componentDidUpdate() for such logic instead.
Example:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor() {
super();
this.state = {
counter: 1
};
}
componentDidUpdate() {
console.log("componentDidUpdate fired");
console.log("STATE", this.state);
}
updateState = () => {
this.setState(
(state, props) => {
return { counter: state.counter + 1 };
});
};
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={this.updateState}>Update State</button>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
this.setState({
isMonthFee: !this.state.isMonthFee,
}, () => {
console.log(this.state.isMonthFee);
})
when i was running the code and checking my output at console it showing the that it is undefined.
After i search around and find something that worked for me.
componentDidUpdate(){}
I added this method in my code after constructor().
check out the life cycle of react native workflow.
https://images.app.goo.gl/BVRAi4ea2P4LchqJ8
Yes because setState is an asynchronous function. The best way to set state right after you write set state is by using Object.assign like this:
For eg you want to set a property isValid to true, do it like this
Object.assign(this.state, { isValid: true })
You can access updated state just after writing this line.

Categories

Resources