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

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));
}

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)

Vuex state empty after reload

Inside a mutation I'm changing my state like:
try {
const response = await axios.put('http://localhost:3000/api/mobile/v3/expense/vouchers/form_refresh', sendForm, {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ###'
}
});
var obj = cloneDeep(response.data);
var temp = cloneDeep(response.data.line_items_attributes.nested_form)
temp = Object.keys(temp).map(key => {
return {
...temp[key]
}
});
obj.line_items_attributes.nested_form = cloneDeep(temp);
state.form = cloneDeep(obj);
console.log(state.form);
} catch (error) {
...
}
So the state shall hold an array with an object as the entry. Checking the state also shows the same. And it's displayed on the view.
When now reloading everything remains inside the state except of the object inside the array. It just shows an empty array inside the store:
line_items_attributes:
attribute: "line_items_attributes"
label: "Positionen"
model_class: "expense_line_item"
nested_form: [] // <---- Object is gone
Nested_form is a hahsmap delivered by the backend. I just turn it to an array. line_items_attribute is a property of the object stored in the state.
EDIT: But it's also not working without the transformation. The assigned state there just doesn't get preserved.
store.js
const store = createStore({
strict: false,
plugins: [createPersistedState()],
modules: {
expense,
invoice
}
});
Calling the action/mutation like:
const updateOuter = (event, refreshable, propertyName) => {
store.dispatch('expense/updateOuterValue', ({
refresh: refreshable,
propertyName: propertyName,
value: event.target.checked ? 1 : 0
}))
};
EDIT:
When changing a different value after calling the mutation the nested_form object is being preserved after the reload.
It seems to work if I call the mutation twice... Any idea how this could be?
The problem was the execution of axios inside the mutation. There must be no asynchronous calls inside a Vuex mutation. As suggested by #e200
You shouldn't do async operations inside mutations, use actions instead.
So it's more than just a best practice, rather a must do.
Explianed here: mutations must be synchronous

Multiple set state within multiple api call inside componentDidMount in ReactJS

I am new in React and trying to call multiple api call within componentDidMount function.
My code is
componentDidMount() {
Promise.all([
axios.get(<url1>),
axios.get(<url2>)
]).then(([res1, res2]) => {
// call setState here
const users = res1.data.users;
this.setState({ users: users});
const banks = res2.data.banks;
this.setState({ banks: banks});
console.log("Users")
console.log(users) // It works
console.log("Banks")
console.log(banks) // It works
})
}
render() {
console.log(this.state.users.length) // Gives length
console.log(this.state.banks.length) // Gives undefined
return (
<div className='popup'></div>
)
}
The problem is inside render function the second state banks length is undefined.
How can I do multiple setstate inside componentDidMount.
Any help is highly appreciated.
Update: Resolved
The mistake was
constructor(props) {
super(props);
this.state = {
users: [],
//MISSING BANKS array
}
}
You should set state in a single update, updating both values at the same time. Otherwise you are instructing React to initiate two renders, the first would contain users value and an undefined value for banks (depending on your initial state declaration). This render would be quickly followed by a second pass, in which both state values users and banks would be defined.
The below example should work as required, in a single render.
Promise.all([
axios.get(<url1>),
axios.get(<url2>)
]).then(([res1, res2]) => {
// call setState here
const users = res1.data.users;
const banks = res2.data.banks;
this.setState({ users, banks });
});
On the other hand, if for some strange requirement you actually want two sequential renders you can use setState's done callback; example below.
this.setState({ users }, () => {
this.setState({ banks });
});
This will ensure the first render is complete before requesting a new render via setState.

How to assign response object to state

I could get data from nodejs backend to react frontend using axios. But I can't assign that object to state object in React.
getData=()=>{
let responseToHandle;
axios.get("http://localhost:9000/api/getData")
.then(function (response)
{
console.log(response.data);//This is working
responseToHandle = response.data;
console.log(responseToHandle);//This is working
this.setState({
data: responseToHandle}, () => {
console.log(this.state.data)
})
})
// HERE IS MY STATE OBJECT
state={
data:[]
}
axios call and this.setState are both asynchronous.
You must add your desired code inside a callback:
this.setState({
data: responseToHandle
}, () => {
console.log(this.state.data)
// Now you can use this.state.data
});
Edit:
You also need to change
axios.get("http://localhost:9000/api/getData").then(function (response) { ... })
to
axios.get("http://localhost:9000/api/getData").then(response => { ... })
Without arrow function the scope inside .then() is the function scope, different from component scope, which means using this gives you a different value.
This code won't work properly:
console.log(this.state.data)
You called a console.log function synchronously right after a promise declaration. Promise works asynchronously.
So in order to check a new state, you should call console.log in render() method, or the best option is to use componentDidUpdate() method.
E.g.
componentDidUpdate(prevProps, prevState) {
console.log(`Previous data: ${prevState.data}`);
console.log(`New data: ${this.state.data}`);
}

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

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.

Categories

Resources