Using setState in reactJs with an object always remains empty - javascript

I have this snippet that doesn't seem to be work and it is driving me insane! Can someone please point out what I have done wrong ?
getInitialState: function () {
return {
modalUser: {},
users: []
};
},
updateModalUser: function (user) {
console.log(user);
module.React.addons.update(this.state, {
modalUser: { $set: user }
});
console.log(this.state);
},
I did try doing this originally without the addons, but I had the same result. i.e. my updateModalUser looked like:
updateModalUser: function (user) {
console.log(user);
this.setState({
modalUser: user
});
console.log(this.state);
},
This output I get either way is:
Object {id: 28, fname:"fred", lname:"flinstone"…}
Object {modalUser: {}, users: []}

this.setState() is async, you need to log the state in it’s callback:
updateModalUser: function (user) {
console.log(user);
this.setState({
modalUser: user
}, function() {
console.log(this.state);
})
}
More info here: https://facebook.github.io/react/docs/component-api.html#setstate

You should use this.setState({modalUser: newObjectHere}), which is the correct way of altering a component's state.

Related

Chrome Extension - Storage Set Specific Key In Object Without Override The Rest Of The Object?

I have this obejct in storage
chrome.storage.sync.set({
user: {
extensionMode: "filter",
list: ["42", "42", "42"]
}
}, () => { });
Now, in a different place, I want to change the extensionMode:
chrome.storage.sync.set({user: { extensionMode: wantedMode }}, () => { });
That's overwriting user.list .
How can I change just extensionMode without overwriting the rest of user?
My best answer so fat is this:
chrome.storage.sync.get("user", (res) => {
let updatedUser = res.user;
updatedUser.extensionMode = wantedMode;
chrome.storage.sync.set({ user: updatedUser }, () => {
// Code
});
});
It works! Just a bit messy and makes the callback hell worse :
Any other ideas?

React: Realtime rendering Axios return

hope you're well. I've been working on a company list component. I am having a problem with updating it in real time, because the axios call I'm sending to 'getById' after it renders is returning only a promise and not the actual data that it is supposed to and I don't have any idea as to why. So when I push the so called new company that I've just added into the array, which is in state, it is only pushing a promise into the array and not the actual Company. I don't have any idea why this is. What the code is supposed to be doing, is it is supposed to be putting the new company into the database, returning the success result, and then I'm using the item from that to make a fresh get call to the axios DB which is supposed to be returning the information I just entered so that I can then insert it into the same array in state that is within the list that is rendered in the company list. However, as I mentioned, only the promise is coming up for some reason.
At one point I was able to get this working, but I did that by essentially calling, 'componentDidMount' after the promise was pushed into the call back clause of the setState funciton of the push function - which was essentially causing the entire component to re-render. I'm a fairly new coder, but my understanding is is that that is a fairly unconventional way to code something, and contrary to good coding methodologies. I believe I should be able to push it into state, and then have it change automatically. I have attached the relevant code below for you to examine. If you believe you need more please let me know. If someone could please tell me why I am getting this weird promise instead of the proper response object, so that I can then insert that into state, I would greatly appreciate it. I've attached some images below the code snippets that I hope will be helpful in providing an answer. I have also left brief descriptions of what they are.
:
class Companies extends React.Component {
constructor(props) {
super(props);
this.state = {
Companies: [],
formData: { label: "", value: 0 },
};
}
componentDidMount = () => {
this.getListCompanies();
};
getListCompanies = () => {
getAll().then(this.listOfCompaniesSuccess);
};
listOfCompaniesSuccess = (config) => {
let companyList = config.items;
this.setState((prevState) => {
return {
...prevState,
Companies: companyList,
};
});
};
onCompListError = (errResponse) => {
_logger(errResponse);
};
mapCompanies = (Companies) => (
<CompaniesList Companies={Companies} remove={remove} />
);
handleSubmit = (values) => {
if (values.companyName === "PPP") {
this.toastError();
//THIS IS FOR TESTING.
} else {
add(values)
.getById(values.item) //I HAVE TRIED IN TWO DIFFERENT PLACES TO GET THE NEW COMPANY IN. HERE
.then(this.newCompanyPush)
.then(this.toastSuccess)
.catch(this.toastError);
}
};
//THIS CODE RIGHT HERE IS THE CODE CAUSING THE ISSUE.
newCompanyPush = (response) => {
let newCompany = getById(response.item); // THIS IS THE OTHER PLACE I HAVE TRIED.
this.setState((prevState) => {
let newCompanyList = [...prevState.Companies];
newCompanyList.push(newCompany);
return {
Companies: newCompanyList,
};
});
};
toastSuccess = () => {
toast.success("Success", {
closeOnClick: true,
position: "top-right",
});
};
toastError = (number) => {
toast.error(`Error, index is ${number}`, {
closeOnClick: true,
position: "top-center",
});
};
This is the axios call I am using.
const getById = (id) => {
const config = {
method: "GET",
url: companyUrls + id,
withCredentials: true,
crossdomain: true,
headers: { "Content-Type": "application/json" },
};
return axios(config).then(onGlobalSuccess).catch(onGlobalError);
};
After the promise is pushed into the array, this is what it looks like. Which is I guess good news because something is actually rendering in real time.
This is the 'promise' that is being pushed into the array. Please note, when I make the same call in post-man, I get an entirely different response, see below.
This is the outcome I get in postman, when I test the call.

How do you run a function when any data in an object is changed using Vuejs?

So I have the following data, and my goal is to recalculate the user's results every time data in this object is changed. Here is the data.
data() {
return {
test: 0,
userData: {
sex: null,
age: null,
feet: null,
inches: null,
activity: null,
goal: null,
},
}
}
Now I have tried to implement both watch and computed, but it seams Vue is not noticing when individual items in the object are changed. However, if I take some data out of the object it does notice the change.
Here is what I tried for watch:
watch: {
userData: function () {
console.log("Changed");
}
}
The result was nothing in the console.
For computed I tried the following:
computed: {
results: function () {
console.log(this.userData);
return this.userData.sex;
}
}
But again nothing was printed in the console.
If I tried with the test variable:
watch: {
test: function () {
console.log("Changed");
}
}
It WOULD output changed when the variable was changed. So that works because it is not an object.
Any help would be very appreciated. Again the goal is to recalculate results whenever any userData is changed.
Here is one way to do it. You need (as #match mentioned) use Vue.set() or vm.$set(). I found it was also necessary to update your watcher property to userData.sex.
new Vue({
el: "#app",
data: {
status: '',
userData: {
sex: ''
},
},
methods: {
updateValues(){
// Or Vue.set()
this.$nextTick( function(){
const newUserData = Object.assign({}, this.userData);
newUserData.sex = "Male";
this.userData = newUserData;
});
}
},
watch: {
userData: function (v) {
this.status += "userData Changed";
},
'userData.sex': function (v) {
this.status += "\nuserData.sex Changed";
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.8/dist/vue.min.js"></script>
<div id="app">
<pre v-text="'userData.sex = ' + userData.sex"></pre>
<pre v-text="status"></pre>
<button #click="updateValues()">
Change Sex
</button>
</div>
EDIT:
Re-assigning the whole object, userData, triggers the watch.userData.
Are you actually using the results property (in your template for example)? Computed properties do not get recomputed if they are not being used.
As opposed to what #match says, I doubt you have a reactivity problem since you do not add or delete properties (they already exist in your data so they are already reactive).

How to tell vuefire's firestore to execute after "created" hook?

I am using vuefire for my new Vue.js project. But there is a problem with initialization...
One of my data property need to be intialized via a method. To do this, I use the created hook of Vue.js. Actually I need the value of this property for my firestore request.
Problem is : the firestore request seems to run before the created hook.
How can I run my firestore requests after the created hook as been run ?
Thank !
data: function () {
return {
currentWeek: null
}
},
firestore: function (currentWeek) {
return {
slots: db.collection('slots').where('start', '>=', this.currentWeek)
}
},
created: function () {
this.currentWeek = moment().startOf('week')
},
this is not the exact code but, basically you want to use the $firestoreRefs to make the query yourself inside of the created lifecycle method
data: function () {
return {
currentWeek: null,
slots : []
}
},
created: function () {
this.currentWeek = moment().startOf('week')
this.slots = this.$firestoreRefs['slots'].where('start', '>=', this.currentWeek)
},
I found that I can solve this issue with watch and binding :
data: function () {
return {
currentWeek: null,
slots: []
}
},
created: function () {
this.currentWeek = moment().startOf('week')
},
watch: {
currentWeek (currentWeek) {
this.$bind('slots', db.collection('slots')
.where('start', '>=', moment(currentWeek).toDate())
}
}
I am pretty sure a better solution can be found...

VueJS return value from promise in method section of component

The function below set the this.personStatus on all components v-bind. How can I make it update a single comm-person component or multiple comm-person components depending on the result of the axios.post?
The template extract for a person is this:
<comm-person
v-for="person in people"
v-show="(!pollActive) || isParticipant(person)"
:participant="pollActive && isParticipant(person)"
:status="personStatus"
:key="person.id"
>
<div class="checkbox-wrap"><input :disabled="pollActive" :value="person" v-model="selectedPeople" type="checkbox"></div>
<div #click="togglePerson(person)" class="name">{{person.given_name}} {{person.family_name}}</div>
<div class="phone-number">{{person.phone}}</div>
</comm-person>
E.G: :status="personStatus"
data() {
return {
pollText: '',
primaryButton: {
border: `2px solid ${iiColor('ii-green')}`,
},
secondaryButton: {
border: `2px solid ${iiColor('ii-grey')}`,
},
people: [],
safeUsers: [],
unsafeUsers: [],
selectedPeople: [],
polledPeople: [],
pollActive: false,
messages: [],
msgType: 'SMS',
personStatus: '?'
};
},
..
..
methods: {
getStatus(person) {
axios.post(`${config.iiApiUrl[process.env.NODE_ENV]}/person/find`, {
id: person.id,
}).then((resp) => {
console.log('handle response...');
console.log(resp.data.ref);
if(resp.data.ref != null) {
if (this.safeUsers.includes(resp.data.ref)) {
console.log('person is safe');
this.personStatus = 'safe';
}
if (this.unsafeUsers.includes(resp.data.ref)) {
console.log('problem with person');
this.personStatus = 'safe';
}
}
else {
return '?';
}
});
},
}
But the this.personStatus from the axios.post call updates all the components. How can I make it update one or two depending on the result?
Please help!!
First of all post returns a promise and it is performed asynchronously. getStatus will return before the anonymous function you pass in to post will be called. It cannot return the string value. For the caller of getStatus() to get the value of the returned string, you must use then(). I suggest you research asynchronous programming and promises in javascript a bit further to understand callbacks and when they are run.
Secondly, you typically should not bind a prop to the result of a method/function. Props almost always should be bound to a data field or computed.
I suggest you change the post handler to set a data field and bind to that instead.
:status="personsStatus"
data: {
return {
person: ??, //i dont know where this comes from
personStatus: 'unknown', //some default value. maybe '?'
};
},
mounted() {
this.loadStatus(this.somePerson);
},
methods: {
loadStatus(person) {
axios.post(...)
.then((resp) => {
//your more complex logic here,
//but instead of returning a string,
//set the personStatus field like the following:
this.personStatus = 'safe';
});
}
}
This may require you to create a child component to do this work if you have multiple person instances.

Categories

Resources