this.state.foo is different in _onChange and different in render()? - javascript

In _onChange method, I wait for a change in this.state.name. On the change _updateArticle method is called:
_onChange() {
var team = this.getTeamState();
this.setState({name: team});
console.log(this.state.name); //"Boston Celtics" //this should have changed but it didn't
this.unbind("articles");
this._updateArticle();
},
then in _updateArticle a new reference is created using this new state.
_updateArticle(team) {
var teamRef = new Firebase(this.props.baseUrl + team + "/results");
console.log(this.state.team); // "Boston Celtics" //this should have been new but its not
this.bindAsArray(teamRef, 'articles');
},
In render method however just to check the state I put console.log to check. In render, this.state.name has been updated properly. My question is why is this.state different outside of render function?.

As per the API 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.
If you really want to access state right after calling setState, use the callback parameter in the setState method:
setState(function|object nextState[, function callback])
So your code should look like:
_onChange() {
var team = this.getTeamState();
this.setState({name: team}, function() {
console.log(this.state.name);
this.unbind("articles");
this._updateArticle(this.state.team);
});
}
Hope this helps.

Related

React JS Updating item in State object to take effect immediately

React JS class component
I know there have been many posts on this subject but I can't seem to get this scenario to work.
Basically on my HandleClickSave event I want to update an item in my object in state without affecting the other values and then passing this updated oblect onto my service to get updated in the db.
The 'item' in question is the 'design' from the (unLayer) React-email-editor.
Problem is after the service is run in 'HandleClickSave' point 3 below, the receiving field 'DesignStructure' in the db is NULL every time. The other two fields are fine as these are saved to state object elsewhere.
Part of the problem is that the Email-Editor doesn't have an 'onChange' property which is where I would normally update the state. The other two values in the object are input texts and they do have an onChange which is how their state counterparts are updated.
This is the object 'NewsletterDesign':
{
"DesignId": "1",
"DesignName": "DesignLayout 1 Test",
"DesignStructure": null
}
In my React class component...
this.state = {
NewsletterDesign: {}
}
And the HandleClickSave event....
HandleClickSave () {
const { NewsletterDesign } = this.state
this.editor.saveDesign((design) => {
this.setState(prevState => ({
NewsletterDesign: {
...prevState.NewsletterDesign,
DesignStructure: design
}
}));
// Update to database passing in the object 'NewsletterDesign'. Field 'DesignStructure' in db is null every time, but other two fields are updated.
NewsletterService.UpdateCreateNewsletterDesign(NewsletterDesign)
etc....etc..
React's setState is not update immediately. read more here.
You can simply do it inside setState by
this.setState(prevState => {
const newState = {
NewsletterDesign: {
...prevState.NewsletterDesign,
DesignStructure: design
}
};
NewsletterService.UpdateCreateNewsletterDesign(newState.NewsletterDesign);
return newState;
});
The setState is an async operation. Meaning, that it's not guaranteed that the new state that you have updated will be accessible just after the state is updated. You can read more here
So in such cases, one of the way is to do the required operation first and then use the result at multiple places.
HandleClickSave () {
const { NewsletterDesign } = this.state
this.editor.saveDesign((design) => {
let newNewsletterDesign = { ...NewsletterDesign,
DesignStructure: design
};
this.setState(newNewsletterDesign);
NewsletterService.UpdateCreateNewsletterDesign(newNewsletterDesign)

React- this shows populated state but this.state is empty

I have a component that renders the user's friends, and I need to get information about them. I have the function below called in componentDidMount that gets information about the friends and puts the data in state:
getFriends = ids =>{
const config = {
headers: {
token: localStorage.getItem('token')
}
};
axios.post('http://localhost:8082/api/friend/getAll', {friends: ids}, config)
.then(res=>this.setState({friends: res.data.friends}))
.catch(err=>console.log(err));
console.log(this);
console.log(this.state)
}
The problem is this shows the correctly populated state:
But this.state shows an "empty" state:
I am confused as to why those 2 are different. It shouldn't be a binding issue because I'm using arrow functions. Any help would be appreciated!
State updates are asynchronous and also the console values are resolved when you expand the object
So for instance when you log this and later when you try to expand the printed object, the state is evaluated and it shows you the updated state since by that time the state got updated.
However when you log this.state the updated value is not shown since the object is already printed on your screen
Please read
Value was evaluated just now with console.log on JavaScript object
setState doesn't update the state immediately
You can see the updated value of state if you log it in the callback function of setState
getFriends = ids =>{
const config = {
headers: {
token: localStorage.getItem('token')
}
};
axios.post('http://localhost:8082/api/friend/getAll', {friends: ids}, config)
.then(res=>this.setState({friends: res.data.friends}, () => {
console.log(this.state);
}))
.catch(err=>console.log(err));
}

React - Children Accessing Parents state too early?

When using a callback from a child to app.js, the callback function in app.js sets the state for an element.
In the same child-function, on the next line, the function accesses this state, which is passed along as a prop.
Is there a way to make sure that this next line executes with the updated state already? If not, whats a better way to implement this?
Child Component
function handleCountrySelected(selected) {
let selectedValues = {
countries: selected,
planningGroups: null,
};
onFilterChange(selectedValues); // changes selectedFilters.country via callback
console.log(selectedFilters.countries); // still old state. selectedFilters is a prop from apps.state. Here it is still showing the old value
filters_changed();
autoApply && applyFilter(); // applyFilter fails because its accessing old state, still.
}
App.js
updateSelectedFilters(selectedValues) {
this.setState({
selectedFilters: {
...this.state.selectedFilters,
...selectedValues
}
}
...
<NavbarTopFilters onApplyFilters={this.onApplyFilters}
selectedFilters={this.state.selectedFilters}
onFilterChange={this.updateSelectedFilters}
/>
setState is an async function. You need to pass in the rest of the lines as a callback.
function updateSelectedFilters(selectedValues, callback) {
this.setState({selectedFilters: {...}}, callback);
...
}
onFilterChange(selectedValues, () => { console.log(...) });

setState doesn't update all state properties after call

The thing is, that I use setState to update state.n and state.data. I want to call setState onClick, using handler. After calling setState, I perform a callback console.log to watch the changed state properties, but only state.data is changed, not state.n.
handler(e) {
this.setState((state) => {
let arr = state.data;
arr.push(fakeTableData[state.n+1]);
this.setState({n: state.n+1, data: arr});
}, console.log.bind(null, this.state.n));
}
After one handler used, I expect to see state.n incremented, but I had only state.data changed, not state.n. And I recieve a warning, like this: "Warning: An update (setState, replaceState, or forceUpdate) was scheduled from inside an update function. Update functions should be pure, with zero side-effects. Consider using componentDidUpdate or a callback." I've read a tutorial on the main react site, but there not enough info.
let's try let arr = [...state.data]
You should not use setState inside function that sets state. That is why you got an error. Instead you should return state. Try this:
handler = (e) => {
this.setState((state) => {
let arr = state.data;
arr.push(fakeTableData[state.n+1]);
return {n: state.n+1, data: arr};
}, () => { console.log(this.state.n) });
}

Ensuring React state has updated for game loop

I am writing a version of Conway's Game of Life in React. The component's state contains the grid describing which of the cells is alive at the current time. In each game loop, the new grid is calculated and the state is updated with the next iteration.
It occurs to me that since setState is asynchronous, when repeatedly calling the iterate function with setInterval, I am not guaranteed to be using the current version of grid each time iterate runs.
Is there an alternative to using setInterval in React that would avoid any potential issues caused by setState being asynchronous?
Here are the relevant functions that describe the game loop:
go = () => {
const { tickInterval } = this.state;
this.timerId = setInterval(this.iterate, 570 - tickInterval);
this.setState({
running: true,
});
};
iterate = () => {
const { grid, gridSize, ticks } = this.state;
const nextGrid = getNextIteration(grid, gridSize);
this.setState({
grid: nextGrid,
ticks: ticks + 1,
});
};
If you need to set state based on a current state, it is wrong to directly rely on this.state, because it may be updated asynchronously. What you need to do is to pass a function to setState instead of an object:
this.setState((state, props) => ({
// updated state
}));
And in your case it would be something like:
iterate = () => {
this.setState(state => {
const { grid, gridSize, ticks } = state;
const nextGrid = getNextIteration(grid, gridSize);
return {
grid: nextGrid,
ticks: ticks + 1
}
});
};
SetState is Asynchronous
this.setState({
running: true,
});
To make it synchronously execute a method:
this.setState({
value: true
}, function() {
this.functionCall()
})
If you have a look at the react official documentation, the setState api does take a callback in following format:
setState(updater[, callback])
Here the first argument will be your modified state object and second argument would be callback function to be executed when setState has completed execution.
As per the official docs:
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.
You can have a look at official docs to get more information on this.

Categories

Resources