Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible edge case in devtools integration: Cannot read properties of null (reading '__VUE_DEVTOOLS_APP_RECORD_ID__') #1630

Open
basuneko opened this issue Sep 6, 2022 · 9 comments
Labels
contribution welcome 🧑‍💻 pkg:devtools Related to devtools vue 2.x Specific to Vue 2 usage

Comments

@basuneko
Copy link

basuneko commented Sep 6, 2022

Reproduction

https://github.com/basuneko/pinia-devtools-issue

Steps to reproduce the bug

  1. Checkout the reproduction repo and run npm run serve
  2. Open a new tab and devtools
  3. Open the reproduction app - you should see the default vue cli homepage
  4. Wait ~60 seconds for the devtools timeout

Expected behavior

  • 'bacon' and 'yolo' stores are listed in the pinia devtools tab and can be inspected
  • the console shows pinia initialisation messages
      🍍 bacon store added
      🍍 yolo store added
    
  • No errors are raised in the console;

Actual behavior

✅ Both 'bacon' and 'yolo' stores are indeed listed in the pinia devtools tab

❌ However, instead of the 🍍 messages, after a timeout, devtools spits out a bunch of errors

Screen Shot 2022-09-06 at 5 31 46 PM

* Error: Timed out getting app record for app at backend.js:1160:14
* [Hook] Error in async event handler for devtools-plugin:setup with args:
* TypeError: Cannot read properties of null (reading '__VUE_DEVTOOLS_APP_RECORD_ID__') at getAppRecordId (backend.js:1103:11)

Additional information

Hi. I'm migrating a Vue 2, Vuex 3, vuex-module-decorators project to Pinia.
For some historic reasons, our store module files export the actual store instance and, while that seems to work with Pinia as well, it seems to break something in the devtools integration. Here's an example:

// @/store/pinia.ts

import { Vue } from 'vue'
import { PiniaVuePlugin } from 'pinia'

Vue.use(PiniaVuePlugin) // it doesn't matter whether it's called here or in main.ts

export const pinia = createPinia()
// @/store/useUserStore.ts
import { defineStore } from 'pinia'
import { pinia } from '@/store/pinia'

const useUserStore = defineStore('user', { ... })

export const userStore = useUserStore(pinia) 

I'm following the Stores outside of components guide, and passing an instance of Pinia into useUserStore. Functionality-wise, this seems to work fine. I have plenty of cypress tests and they're all passing after the migration. But the devtools errors are mildly concerning.

Vue 2.7.5 -> 2.7.10
Pinia 2.0.21
Devtools extension 6.2.1
Chrome 105.0.5195.52

@posva
Copy link
Member

posva commented Sep 6, 2022

I cannot reproduce but the error comes from the devtools anyway. Make sure you have the latest devtools version installed and not the beta. I managed to reproduce it.

As a side note, I discourage you from doing this:

const useUserStore = defineStore('user', { ... })

export const userStore = useUserStore(pinia) 

Instead, use the patterns shown in docs with setup() or with mapStores() (& co). It's important the useStore(pinia) are called after new Vue(). If anybody wants to give this a shot, go ahead

@posva posva closed this as not planned Won't fix, can't repro, duplicate, stale Sep 6, 2022
@posva posva reopened this Sep 6, 2022
@posva posva added vue 2.x Specific to Vue 2 usage 🧑‍💻 pkg:devtools Related to devtools contribution welcome labels Sep 6, 2022
@DallasHoff
Copy link

+1, I encountered this when trying to use a Pinia store in a Vue Router navigation guard in a Vue 2.7 app, importing the Pinia instance from main.ts.

@jorismak
Copy link

jorismak commented Dec 9, 2022

I'm a bit confused by this.

But how do we set the state on the store to some initial stuff (like from an API) before 'loading the app' (doing the very first new Vue()).

In my code I have this:

Vue.use(PiniaVuePlugin);
const pinia = createPinia();

const mainStore = useMainStore(pinia);

mainStore.refreshUser().finally(() => {
    new Vue({
        router,
        vuetify,
        pinia,
        render: (h) => h(App),
    }).$mount("#app");
});

To make an axios call to see if we are logged in or not, and store that in the store, and only then load up the very first Vue component. This way the Vue app knows from the very first moment if it has a user or not and to go to a login-page or not.

I used to do this method with vuex without issues. Is it just the devtools that are having issue with this or do I have a problem in my code I didn't encounter yet?

@lee1nna
Copy link

lee1nna commented Mar 15, 2023

I'm having the same problem, is there a solution? 😥

@jorismak
Copy link

I still had cases of using it outside a component without giving the instance from my main.ts. like in a router hook.

Carefully looking it all through and fixing it , seemed to fix it. But now after a while it's back.

@BoxenOfDonuts
Copy link

@basuneko I wound up here because I was having a similar issue, turns out I had a useXyzStore() running before Pinia was installed, a similar example here

I refactored your example a bit to get rid of the errors, it works exactly the same way but I got rid of these:
export const userStore = useUserStore(pinia)

and just did a normal import of the store:

// in HelloWorld.vue
import { useBaconStore } from '@/useBaconStore';

You can see a diff here

@c-malecki
Copy link

c-malecki commented Apr 25, 2023

@jorismak

I'm a bit confused by this.

But how do we set the state on the store to some initial stuff (like from an API) before 'loading the app' (doing the very first new Vue()).

In my code I have this:

Vue.use(PiniaVuePlugin);
const pinia = createPinia();

const mainStore = useMainStore(pinia);

mainStore.refreshUser().finally(() => {
    new Vue({
        router,
        vuetify,
        pinia,
        render: (h) => h(App),
    }).$mount("#app");
});

To make an axios call to see if we are logged in or not, and store that in the store, and only then load up the very first Vue component. This way the Vue app knows from the very first moment if it has a user or not and to go to a login-page or not.

I used to do this method with vuex without issues. Is it just the devtools that are having issue with this or do I have a problem in my code I didn't encounter yet?

Sorry for digging this up awhile after you posted, but did you ever find a solution for this? This is the exact same situation I'm facing currently.

@jorismak
Copy link

Well, the code is working fine. For me it's in a SPA , so it's client side only. The weird errors in the devtools don't seem to hinder the app or the devtools... It just hints at a pattern that's discouraged , specially for SSR leaking . Since this is for me without SSR , i have no issue with it.

I just don't see a better pattern to so this.

Thinking out loud now: i want the store to have a valid 'logged in or not' state before the first route hits. If you do it later, you get a flash of the page you are visiting, and then the route guard kicking in and getting redirected to the login route after seeing a flash of something else (or some sort of loading indicator ). I didn't want that , so i refresh my logged in state before doing the first new Vue().
Now, isn't it an option to do this refreshUser call in a onCreated or another lifecycle hook of the very first root Vue object ?
In that case Vue would've been 'started' and pinia would have a valid unit i guess.
You still have to be careful to always give the pinia context to any 'useStore' you use outside of a script-setup.

The thing is that my refreshUser call is async, it returns a promise . I basically want that promise to resolve (or error ) before attempting to create and mount the first route. I don't know if this is possible .

Another completely different method is to embed that initial state data in the initial server request that returns the html to start your app . There could be a script tag with some json inside it, and pinia can maybe fetch that json data to setup initial data in a store. 'hydration'. If pinia has no automated way for this , your code in your store can do it manually as a last resort. (Use document.getElementById to find a script tag with an id, use inner text/html to get the json inside. And set your default store values depending on this object, if it all works out.

But this means that your backend must generate the html dynamically . And normally i just have a static index.html.

In a SSR situation - which means nuxt3 for me - i just use the pinia nuxt module. The refreshUser call is there in a nuxt middleware, which bypasses this whole problem. (do i have a valid user in the store ? Do nothing . If not, check if we called refreshUser at least once. If we did, that means we redirect to login. If we now have a valid user after calling refreshUser , continue like normal).

I basically use 'user == undefined' for 'store is not initialized yet, 'user == null' for no user logged in, and otherwise we have a valid user .

@weifenghong
Copy link

It seems that it can be executed in nextTick?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contribution welcome 🧑‍💻 pkg:devtools Related to devtools vue 2.x Specific to Vue 2 usage
Projects
None yet
Development

No branches or pull requests

8 participants