Page only loading after I click, then go back in the browser, then click again

Hi All, I have been stuck on this challenge for a while. I am trying to load a user’s profile page, however the page doesn’t initially load when I click one of the users from the user list. When I click back in the browser, then click the user again, it now loads the profile correctly. I see in my console that it appears to be fully loading the store data only after I click back and forth (as described above). Also profiles with incomplete data never load.

I am dispatching the action to get the users data in the created hook and have also explored pulling the data through a computed property.

The component is pretty large so I’m not sure which part I should be pasting in here to provide reference.

Sprinkle console logging throughout to establish what is going on and precisely where it stops doing what you’re expecting. Once you find the first bit that isn’t working, post the code for that and whatever is supposed to trigger it.

It’s probably the store code we’ll need to see, not the component, unless the action isn’t being called at all.

As a guess, it could be a reactivity issue caused by adding properties to an object without using Vue.set. If you’ve got an object in your store that holds the profiles keyed by id then you’ll need to use Vue.set when adding a new profile to that object.

Thanks for the info skirtle. I have been exploring this and having some trouble using the Vue.set. It’s my first time seeing this.

When I click from the page of users to the individual profile page, the console log for (this.trainingDataArray) shown in the code below just shows [ob: Observer] length: 0

Then when I click back to the user list page, then back to any of the profile pages, the console log populates with all the training data.


Here is the is code from the UserView.vue page where I load in the training data.

// Find Current User and access training session data
database.ref(‘TrainingsByUser/’+ this.currentUser.uid)
.on(‘value’, snapshot => this.usersTrainingData = snapshot.val())
this.trainingDataArray = Object.values(this.usersTrainingData)
console.log(this.trainingDataArray)

I also have this action in the mounted () section, which I have also tried in created () and most of the other lifecycle hooks.

this.$store.dispatch(‘userManagement/userTrainingData’).catch(err => { console.error(err) })


In my userManagementActions.js file I have the following code:

userTrainingData ({ commit }) {

// Find Current User and access training session data
database.ref('TrainingsByUser/').on('value', snapshot => {
 snapshot.val();
 const usersTrainingData = snapshot.val()
 const usersTrainingDataArray = Object.keys(usersTrainingData)
  console.log(usersTrainingDataArray)
  commit('SET_TRAININGS', usersTrainingData)

})
},


Does this help?

It looks like there’s a problem with that first section of code.

The on handler will be called asynchronously, so the code that follows won’t run until too late.

To see what I mean, try adding some logging to the handler:

database.ref('TrainingsByUser/' + this.currentUser.uid)
  .on('value', snapshot => {
    this.usersTrainingData = snapshot.val()
    console.log('inside on', this.usersTrainingData)
  })

The key thing to note is the timing of the logging relative to the other logging. You should find that this new logging occurs after your existing logging, which is too late.

You could move the calculation of traingingDataArray inside the on handler:

database.ref('TrainingsByUser/' + this.currentUser.uid)
  .on('value', snapshot => {
    this.usersTrainingData = snapshot.val()
    console.log('incide on', this.usersTrainingData)
    this.trainingDataArray = Object.values(this.usersTrainingData)
    console.log(this.trainingDataArray)
  })

Or, if possible, change trainingDataArray to be a computed property instead.

So I tried using this code you suggested:

database.ref(‘TrainingsByUser/’+ this.currentUser.uid)
.on(‘value’, snapshot => {
this.usersTrainingData = snapshot.val()
console.log(‘inside on’, this.usersTrainingData)
this.trainingDataArray = Object.values(this.usersTrainingData)
console.log(this.trainingDataArray)
})

And it logged the same thing before and after loading the page. It’s strange because everything on the page doesn’t load until I click back, then go back to the user’s page.

I am using a computed property for the training data as well, which is am using in a v-for loop to display all the trainings on the page (which works after I click back and forth):

trainingData() {
const trainings = this.$store.state.userManagement.trainings
const usersTraining = trainings[this.currentUser.uid]
const trainingDataArray = Object.values(usersTraining)
return trainingDataArray.reverse()
},

I do see that when I originally load the User List page I don’t see the array of users (but they still do appear in the list of users). Then when I click back from the users profile, to the user list page again, the array loads.

This is the code on the user list page that only loads after I go back and forth:

this.userKeys = this.$store.state.userManagement.users
console.log(this.userKeys)

I also have a computed property that is returning the list of users from the same part of the store.

computed: {
usersData () {
return this.$store.state.userManagement.users
console.log(usersData)
},

**
Also, not sure what I am doing wrong to make the text I am pasting show up as code here.

I don’t understand what that means. What does ‘before and after’ mean? Do you mean it logs everything twice? It would also help if you could say exactly what is being logged rather than using relative terms like ‘the same’.

I’m generally struggling to follow your descriptions of what you’re seeing. It would help if you could frame your observations in terms of the code. It might be clear to you what you mean by ‘list of users’ but it’s really difficult for me to figure out exactly what that’s referring to. If you instead word it as ‘the console logging of X is logging an empty array’ it’ll help to remove any doubt or ambiguity.

If you haven’t already tried it then make sure you’ve put in console logging to check this.currentUser.uid prior to using it in that database.ref.

Beyond that, my advice remains the same as it was originally. Sprinkle console logging around and try to find the first thing that isn’t doing what you expect. Focus on that. Keep working back. If a value is incorrect then try to work back to the code that was supposed to set that value. Keep going until you either find the problem or you hit the piece of code that isn’t working but you don’t know why. Once you get to that stage, post that piece of code here with a detailed explanation of what you’re expecting and what’s actually happening.

There are two pages I am working on. One is the dashboard page that shows the list of users:


The second is the profile page of the user that shows their training data:

I have continued to experiment with sprinkling console.logs throughout my code. I have noticed on the page that shows the list of users, when I first load that page, the users don’t load from the store. This is the code I am using to log if they have data:

    this.userKeys = this.$store.state.userManagement.users 
    console.log(this.$store.state.userManagement.users)
    console.log(this.userKeys)

Both of these logs show [__ob__: Observer] with an array length of 0. When I click to another route, then back to the same page, it then shows the array of users like this (12) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, __ob__: Observer] where I can now see everyone.

It appears that if some value is null or hasn’t loaded, the entire profile page won’t load. For example, where it says “passed tutorial, passed training, passes exam” if any of those values are false, the entire page won’t load.

OK, I think I follow where the problem is now.

If I’ve understood correctly…

You’re dispatching userTrainingData in a hook when the page shows. That’ll load the data asynchronously, so it won’t be populated immediately. In the meantime state.userManagement.users will still have its initial value, which is an empty array.

You then assign this store state to a local userKeys property in your component. I’m not sure exactly where you do that but I assume it’s also in one of the lifecycle hooks. Wherever it is, you’re doing it before the data has loaded from the server. So the value you’re assigning is still the initial empty array.

The data will then load and populate the store but there’s nothing to tell the component to update userKeys, so that is still an empty array.

When you go back and forward between the pages it goes through this process again, except that the value in the store state is no longer the initial empty array. Instead it’s the correctly populated array from the previous visit.

The most natural way to fix this is to make userKeys a computed property so that it updates when the store updates:

computed: {
  userKeys () {
    return this.$store.state.userManagement.users 
  }
}

You should also remove userKeys from the data section.

When the page first loads, userKeys will be an empty array. Your UI will need to deal with that appropriately. However, because you’re using a computed property it should now automatically update when the store state is updated after the request comes back from the server.

On a separate note, you’re going to need to be careful to tidy up your database listeners. When you register a listener with on it will fire every time the data changes from that point forward. It isn’t a one-time callback. If you visit the page multiple times it’ll keep adding listeners if you don’t do anything to prevent that. You probably won’t notice any difference until the data changes and they all fire at once.

Hey thank you for all the info here. I have spent the last few days experimenting with this and I have found that when I remove UserKeys: [], from the data object, the entire page won’t load at first. Again, if I click back and forth from another route it will load.

I did all of this while implementing the UserKeys computed property as you suggest above, and it does deliver the right value after clicking back and forth between routes. But at the first load, nothing shows when using this method. If I keep the computed property, and also add back in UserKeys: [] in the data object, the page does load at first but doesn’t populate with the array data. I have used the computed values in other parts of this app successfully. I’m not sure what else to do here.

Please give specifics. What errors are shown? Did you put console logging inside the computed property to confirm what value was being returned? Does that logging get triggered again after the data is loaded into the store?

I would expect the initial value to be null or undefined or an empty array, depending on what you have in your initial store state. Check you do have it defined in the initial store state or it won’t be reactive and nothing will update when it does turn up.

I was able to get it to work by dispatching the action on the prior page so it was already loaded before it clicked through. I tried the computed properties in many ways and it still showed up with nothing in the array. It is technically working, unless someone refreshes the profile page, then it comes up blank until they go back to the user list page. Thanks for your help. I will continue to explore this.

Hey there, hope you have been doing well. I have another question for you if you’re up for it.

I am now getting an object back from the Firebase Realtime database with a long list of all the training sessions a user has gone through (in the below example the individual training session main node is -MKNF…) Contained in many of the individual training sessions is a Mistakes object, which then has 1 or more nested objects with the key set to a timestamp of when the mistake was made relative to when the training started. Inside each timestamped object are three more key and value pairs (display, mistakeName and stepKey). You can see the whole data structure here:

I have used the following code to create an array of all the training sessions, then filtered them to only show sessions where the mistakes object is not undefined:

database.ref('TrainingsByUser/'+ this.currentUser.uid)
      .on('value', snapshot => {
          this.usersTrainingData = snapshot.val()
          this.trainingDataArray = Object.values(this.usersTrainingData)
          this.allMistakes = this.trainingDataArray.map(a => a.Mistakes) 
          var onlyMistakes = this.allMistakes.filter(e => e !== undefined)
          console.log("mistakes", onlyMistakes)
      })

This is working fine and I have an array with 20 training sessions with similar structures to the image pasted above.

Now I am trying to figure out how to count all the values of stepKey to figure out which mistakes are made most often. Ideally I would be able to have a list of the most common to least common mistakes made. Then eventually I will display the most common mistakes on the page.

I am not sure how to query past the timestamp since it’s randomly generated and I can’t query it directly with a preset value.