First I check if Localstorage is supported, if it is I check if there is an item query in localstorage. If there is a item it should update to the query state. Before setState I have an alert and it alerts the item fine. But after the setState I alert again but it displays nothing? Somebody knows why? Even if I replace this.setState({query: localStorage.getItem("query")}) with this.setState({query: 'test'}) it doesnt display test in the alert?
getLocalStorage = () => {
if (typeof(Storage) !== "undefined") {
if (localStorage.getItem("query") !== null) {
//Alerts 'a string'
alert(localStorage.getItem("query"));
this.setState({
query: localStorage.getItem("query")
});
//Alerts nothing?
alert(this.state.query);
this.search();
}
else{
this.getLocation();
}
}
else {
this.getLocation();
}
};
setState won't show direct change it will take time :
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.
If you want state value just after it's set you have to use it like this :
this.setState({
query: localStorage.getItem("query")
},() => {
alert(this.state.query);
});
For more details , please read setState
setState is asynchronous method so that way you are geting this kind of response. So for this bug you can see -
Is there a synchronous alternative of setState() in Reactjs
Related
Below is the initial state which is assigned
state = {
movie : null,
actors : null,
directors : [],
loading : false
}
Below is the set state which i set and at the end to set state as put in comment when it is set and not set. I'm not able to access the this.state outside.
this.setState({movie:result},() => {
//then fetch the actors
const endpoint= `${API_URL}movie/${this.props.match.params.movieId}/credits?api_key=${API_KEY}`;
fetch(endpoint)
.then(result => result.json())
.then(result=>{
const directors =result.crew.filter((member)=> member.job==='Director' );
this.setState({
actors : result.cast,
directors,
loading : false
})
console.log(this.state); //here value is coming
})
})console.log(this.state); //here value is not coming
How to get this.state value with updated info?
render() {
//removed code below mull div
return (
<div className="rmdb-movie">
{this.state.movie ?
<div>
<Navigation movie={this.props.location.movieName}/>
</div>
: null
}
</div>
)
}
What you're experiencing is the result of using asynchronous functions. The callback you pass to this.setState() is called whenever the state is set, and there might be a delay between when you call this.setState() and when the state is actually updated. This means that the callback may or may not be executed when you reach the second console.log().
This explains why you are seeing these results. The first console.log() is actually called after the second console.log.
This is react lifecycle ! The setState function is not executed instantaneously. The inner console.log prints the good values because in the callback of setState you are sur it is executed. The outer console.log is executed before setState has actually modified the state and so you don't see the updated info.
What you are seeing is actually what is expected, this is because Javascript in general and your specific fetch API call is asynchronous. Your first console.log() is inside the response from the fetch request, so it is only executed when the server returns the response(which could be a few seconds or more), then the state is set, then you print using console.log. The second console.log is outside, and will actually print instantly ,even before the async API call is made, before the state has actually been updated. Therefore, I would recommend that whatever action you want to do after the state is set, should be done where the first console.log is. Or preferably like this in a callback that you can pass to this.setState to ensure the state has been updated.
this.setState({movie:result},() => {
//then fetch the actors
const endpoint= `${API_URL}movie/${this.props.match.params.movieId}/credits?api_key=${API_KEY}`;
fetch(endpoint)
.then(result => result.json())
.then(result=>{
const directors =result.crew.filter((member)=> member.job==='Director' );
this.setState({
actors : result.cast,
directors,
loading : false
},()=>{
// API Call has returned and state has been updated
// Now access this.state
});
})
})
setState operation is asynchronous, you can read more about it in react documentation.
What problem are you trying to solve? Probably you need to use hook componentDidUpdate or just use this value in next render call.
I think you should rethink the code because there is no way result will get to movies the way you put it. Except somehow you update another state inside the API function to fetch movies. So somehow you can call fetch which you must have binded to your class
this.fetch = this.fetch.bind(this) from some onclick or onchange or something.
fetch =() =>{
const endpoint= `${API_URL}movie/${this.props.match.params.movieId}/credits?api_key=${API_KEY}`;
fetch(endpoint)
.then(result => result.json())
.then(result=>{
const directors =result.crew.filter((member)=> member.job==='Director' );
this.setState({
actors : result.cast,
directors,
loading : false,
movies:result
})
})
}
Where should we ideally place an api call to be made on occurrence of an event in React
Inside the eventHandler or componentDidUpdate ?
example:
handleItemClick = (item) => (event) => {
this.setState({selectedItem: item});
this.props.requestDataActionDispatch(item);
}
OR
componentDidUpdate(prevProps, prevState, snapshot) {
if(prevState.item !== this.state.item) {
this.props.requestDataActionDispatch(item);
}
}
Depends
But a simple solution is, if you want to call some API after change of state value then you must go for eventHandler. Also check for callback in setState.
handleItemClick = (item) => (event) => {
this.setState({selectedItem: item}, () => this.props.requestData(item));
}
I don't see any reason to wait for component update, I'd just put it in the event handler.
Naturally, in either case your component needs to know how to render appropriately when it has a selected item but doesn't have the data from the API yet...
(Side note: If requestDataActionDispatch results in a state change in your component, you probably want to clear that state when setting the selected item prior to the request, so you don't have one item selected but still have the state related to the previous item. But I'm guessing from the fact it's on props that it doesn't...)
It depends
I would prefer to call the api inside componentDidUpdate. Why ? Because it's cleaner. Whenever there is a change in state or props, componentDidUpdate will be called. So definitely, there has to be a condition inside componentDidUpdate like you have mentioned
if(prevState.item !== this.state.item)
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.
I am using the code below:
makeRemoteRequest = () => {
let items = [];
models.forEach(element => { //models is an array of the list of models
this.pushReports(element, items);
});
console.log("This is the item array: ",items);
this.setState({
data:items
});
console.log("This is the data in state: ",this.state.data);
}
Somehow, the console log for the items array is showing me the array that I need, but the console log for the this.state.data is empty. How can this be possible? The log for the items array is run right before state is set.
This is preventing me from updating my state.
this.setState is rendering asynchronously. And you're trying to print in next line so it will not give immediate results as you want.
Solution: do this in next line,
setTimeout(() => {console.log("This is the data in state: ",this.state.data) }, 1000)
this.setState() does not run synchronously. Your state is not guaranteed to be updated on the next immediate line, but it will be updated properly on the next render cycle. Try putting console.log within render() and you'll see.
Discussion about this topic here: https://github.com/facebook/react/issues/11527#issuecomment-360199710
Since setState works in an asynchronous way. That means after calling setState the this.state is not immediately changed. So if you want to perform an action immediately after setting state, use 2nd argument as callback on setState. Consider this example:
this.setState({
data: newData
}, () => {
//TODO: Do something with this.state here
});
I'm getting a very strange issue that I don't know how to address.
I've got a fairly straightforward method: When a button is pressed it called toggleInverted()
toggleInverted() {
if(this.state.inverted){
this.setState({inverted: false});
} else{
console.log("got to else...");
this.setState({inverted: true});
console.log(this.state.inverted);
}
}
The inverted field is initialized in the constructor to false. However the first time I click the button when I load the page, it doesn't correctly reset the state. The output is:
got to else...
false.
So somehow it is getting into this else statement, executing the setState, and yet not setting inverted to be true...
Is there something about setState that I am missing?
setState does not change your components state immediately. Your state change may be asynchronous and thus no guarantee that your console.log statement will print the changed state. setState however does always lead to the render method being called, so if you console log your state inside the render method of your component, your state should be true.
Also, this.setState({inverted: !this.state.inverted}) is a shorter version of your code snippit
setState may be asynchronous
Meaning you cannot expect the values of this.state to change immediately after setState is called.
To quote the 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...
As an aside, you can figure your code by using the negative expression of the current inverted value as follows:
toggleInverted() {
this.setState({inverted: !this.state.inverted});
}