calling setState method with other statements - javascript

I know that calling setState method means I don’t have to manually invoke the ReactDOM.render method. Below is some example code:
...
render() {
return (
<button onClick={this.handleClick}>
Click
</button>
)
}
handleClick = () => {
this.setState({ counter: this.state.counter + 1 }, () => this.setState({ hasButtonBeenClicked: this.state.counter > 0 }));
this.props.callback();
}
since there is another statement this.props.callback(); below the this.setState() method, so does ReactDOM.render method get called before or after this.props.callback();?

since there is another statement this.props.callback(); below the this.setState() method, so does ReactDOM.render method get called before or after this.props.callback();?
After, in your case. The state update and call to render is asynchronous if setState is called within a React event handler (and may be asynchronous even if not, more here). The sequence (within a React event handler) is:
You call setState with the state update.
You call this.props.callback();
React processes the state update (possibly combining it with other pending updates).
(Some handwaving here about shouldComponentUpdate.)
React calls render to have it render the current state.
If you want this.props.callback(); called after the new state has been rendered, put it in a function as the second argument of setState:
handleClick = () => {
this.setState(
{ counter: this.state.counter + 1 },
() => {
this.props.callback();
}
);
}
or
handleClick = () => {
this.setState(
{ counter: this.state.counter + 1 },
this.props.callback
);
}
...if this.props.callback doesn't care what this you call it with.
More here:
State Updates May Be Asynchronous (that title drives me nuts, because it's not that they may be asynchronous, it's that they are asynchronous)
setState API docs

If you look at my example of your code, this.props.callback() gets called immediately after the state has been updated.
handleClick = () => {
this.setState({ counter: this.state.counter + 1, hasButtonBeenClicked: true }, () => this.props.callback() );
}
You have chained setStates, which seem unnecessary for readability. These should be grouped into a single setState call automatically, however one is nested into the callback and this would trigger multiple re-renders.. depending on ShouldComponentUpdate.
To avoid guessing or leaving it susceptible to future React updates:
Using the setState callback for this.props.callback, is the best way to ensure it is executed after setState completes.
Updated, based on T.J Crowders feedback and research with event handlers:
The way your code was structured it is most probable that this.props.callback will be called prior to the setState actually completing state updates, it will trigger the re-render once state updates.
-Because, it is in an asynchronous call and within a React event handler. SetState should update state after.(I am still no expert on promises, and have to wonder if there is a chance state could update within the millisecond, depending on your browsers internal clock and the batch processing)
For clarity, to others. This example is asynchronous and that means the code continues to be executed, while waiting for resolution. While setState returns a promise. In this case, it should absolutely continue to process this.props.callback(), until the promise is resolved.

Related

Why don't React component functions break/terminate when state is changed?

function App() {
const [buttonClick, setButtonClick] = useState('No')
async function handleClick(){
setButtonClick('Yes')
const sleep = (ms) ={return new Promise(resolve => setTimeout(resolve, ms))}
await sleep(2000)
console.log('this ran after state was changed')
}
return(
<>
<button onclick={handleClick}>Click me</>
{buttonClick}
</>
)
}
How come if I click the button, which sets the state of buttonClick, does it not break out of the function? It still sleeps for 2 seconds then logs my message in the console.
My thinking is that since it causes a component re-render, it would lead me to believe that the function App is returning and would break out of the handleClick function.
My thoughts on why this might be is that it might be when React is compiled it is just storing the value of the return somewhere else, and not actually returning the broader function App().
This has more to do with how Javascript works rather than React, I'll do my best to explain:
When you click the button your handleClick function is invoked.
handleClick is now on the call stack and will not terminate until either a value is returned or an error is thrown.
The first instruction in handleClick is to update the state, however, updating the state will not terminate the invokation of handleClick and even if the component were to unmount before it had a chance to finish, any call to setButtonClick on an unmounted component would result in what is known as a memory leak.
Furthermore the setTimeout and async nature of this function will end up in various parts of the event loop, but that has little to do with the original question.
You can learn more about the nature of Javascript functions here.

Confusion with how setState works

I am confused with how setState works.
Here is a portion of my code:
handleBarsChange(value) {
this.setState({numberOfbars: value});
this.resetArray();
}
apparently, when the resetArray() function executes and tries to access the this.state.numberOfbars state variable, setState hasn't updated the value yet.
I want some explanation on how and where should the setState be executed and when can i expect to see the change.
Thank you!
setState is asynchronous. It has a second callback argument that can be used to run code after a state transition. This isn't always the best approach, but with the very narrow example provided, it's hard to say.
this.setState({numberOfBars: value}, () => { this.resetArray() })
From the react docs:
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.
https://reactjs.org/docs/react-component.html#setstate
The fact is that setState() function is async so even if you run code after you call it, some state may not be updated yet.
The solution is implement a callback when the state was updated succesfully (setState(<state>, <callback>)), for example:
handleBarsChange(value){
this.setState({numberOfbars: value}, () => {
this.resetArray();
});
}

Changing the state with returned JSON data

i am trying to get data from an external API and use it to change the state on my app. The data actually shows up in the console but when i run set state does not change the state of my app.
class App extends Component {
state={
jobs:[]
}
onTermSubmit=(term)=>{
const proxy=`https://cors-anywhere.herokuapp.com/`;
const api = `${proxy}https://--------/positions.json?description=${term}&page=1`;
fetch(api).then(res=>res.json()).then(data=>this.setState({jobs:data}))
console.log(this.state)
}
I am trying to get the state to change to the data returned from the API
Both fetch() and setState() are asynchronous so attempting put a console.log() statement after the expression will not wait for the fetch or setState() to complete/resolve. Instead you can use the callback argument of setState() to console.log() only after fetch has resolved and setState() has updated state. From the documentation:
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.
fetch(api)
.then(res => res.json())
.then(data =>
this.setState({ jobs: data }, () => {
console.log(this.state);
})
)
.catch(err => console.log(err));
Hopefully that helps!
As mentioned by #Alexander Staroselsky fetch() and setState() are asynchronous operations and the application won't wait until they are done to continue to the next operation thus resulting in this.state.jobs to still be an empty array.
My solution would be to make them synchronous by adding the async/await keyword before calling he function like this.
onTermSubmit= async (term)=> {
const proxy=`https://cors-anywhere.herokuapp.com/`;
const api = `${proxy}https://--------/positions.json?description=${term}&page=1`;
let rawRes = await fetch(api)
let jsonRes = await rawRes.json()
await this.setState({jobs:rawRes}))
console.log(this.state)
}
Just another approach
Hope this is helpful

RxJS and React's setState - delay function execution until subscription

RxJS has a nifty function, fromCallback that takes a function whose last parameter is a callback and returns an Observable. And I want to combine that with React's setState function so that I can do something analogous to:
const setState = Rx.Observable.fromCallback(this.setState);
setState({ myState: 'Hi there!' }).concat(....)
so that any operations chained to setState are guaranteed to happen after the state has been set and, most importantly, that setState isn't invoked until there's an active subscriber.
What I noticed though is that even without a subscribe, setState is being called right as it's defined and setting the state of my component. So if I have:
networkSignal.flatMap((x) => {
return setState({ myState: 'test' });
});
the function setState is immediately invoked but the observer it producers won't send a next until there's a subscriber. What I want is for the function to only invoke when there's a subscriber.
Looking into the source you can see that RxJS returns a function that when executed, creates an observable but immediately invokes the function - the callback argument.
fromCallback returns a function which, when executed, returns an observable. That observable is where the asynchronous result(s) of the function call will flow.
To delay the execution of the function, you can make use of .defer. For instance:
const setState = Rx.Observable.fromCallback(this.setState);
const deferred$ = Rx.Observable.defer(function (){return setState({ myState: 'Hi there!' }).concat(....)});
// Later on
deferred$.subscribe(...)
Question whose answers used the same technique were asked here and here

Fulling updating setState in reactjs

In the reactjs docs for setState:
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.
The second (optional) parameter is a callback function that will be
executed once setState is completed and the component is re-rendered.
What if I just wanted to update my state, do I create a callback that does nothing?
As they write setState() does not immediately mutate this.state but creates a pending state transition. because it works in an asynchronous way. So if you want to perform an action immediately after setting state on a state variable then a callback will be useful.
For Example
setState(
{ name: "Hello World" },
() => console.log(this.state)
);
The callback is optional so you can do this.setState({ key: value });.

Categories

Resources