Search code examples
vuejs3pinia

"getActivePinia()" was called but there was no active Pinia. Not sure how I'm using my store wrong


I encountered this error when trying to implement a game loop for my game. I'm using Pinia to store the global state of my game which I've created like so:

baseCamp.js

import { defineStore } from "pinia";
import { ref } from "vue";

export const useBaseCampStore = defineStore("baseCamp", () => {
  const baseCamp = ref({
    artifactSeed: 1,
    population: 0,
    workingPop: 0,
    resources: {},
    prestigeMaterial: {},
    availableJobs: {},
    industry: {
      buildings: {},
      equipment: {},
    },
    research: {},
    stats: {},
  });

  const increaseResource = (resource, quantity) => {
    if (!baseCamp.value.resources[resource]) {
      baseCamp.value.resources[resource] = 0;
    }

    baseCamp.value.resources[resource] += quantity;
  };

  const formatNumber = (number) => {
    return parseFloat(number).toFixed(0);
  };

  return { baseCamp, increaseResource, formatNumber };
});

I have created my pinia instance in main.js along with my vue app.

import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";

const pinia = createPinia();
const app = createApp(App);

app.use(pinia);

app.mount("#app");

And brought all of that into my App.vue file where I try to initiate the loop.

App.vue

<template>
  <StateTest />
</template>

<script>
import { onMounted } from "vue";
import { mainLoop } from "./gameLoop";
import StateTest from "./components/StateTest.vue";

export default {
  setup() {
    onMounted(() => {
      requestAnimationFrame(mainLoop);
    });
  },
  components: {
    StateTest,
  },
};
</script>

gameLoop.js

import { useBaseCampStore } from "@/store/baseCamp";

const store = useBaseCampStore();
const { increaseResource } = store;

var lastTime = null;
var totalTime = 0;

export function mainLoop() {
  const currentTime = performance.now();
  if (lastTime === null) {
    lastTime = currentTime;
  }
  const deltaTime = currentTime - lastTime;

  updateGame(deltaTime, totalTime);

  totalTime += deltaTime;
  lastTime = currentTime;

  //console.log("it ran");
  requestAnimationFrame(mainLoop);
}

// eslint-disable-next-line no-unused-vars
function updateGame(deltaTime, totalTime) {
  increaseResource("wood", (1 / 1000) * deltaTime);
}

The code compiles and will run if I leave out the onMounted() function in App.vue. It will even run if I first open it with the onMounted portion commented out and then uncomment it and it works fine. I've gathered from searching online and reading several different posts that I'm somehow trying to access my store before the Pinia instance has been created but I don't quite understand why that's the case. Does main.js not need to run first before App.vue can be interpreted?

These are the posts I've read but don't really understand their answers and haven't been able to implement either answer into my code successfully.

getActivePinia was called with no active Pinia. Vue

"getActivePinia() was called but there was no active Pinia. Did you forget to install pinia?" error in console, but the app compiles successfully

I have also tried using <script setup()> in App.vue as I read that composables are not compatible with the options API, but then I get an error when trying to import my StateTest component that won't compile.

Ultimately I'd like to fix my code, but I think I'm having a fundamental misunderstanding of what is happening behind the scenes.


Solution

  • When importing a module, codes at the top level will always run first. Because useBaseCampStore() is at the top level of a module, when useBaseCampStore is called, Pinia hasn't been installed to Vue app instance.

    It's better to call hooks in a function.

    function updateGame(deltaTime, totalTime) {
      useBaseCampStore().increaseResource("wood", (1 / 1000) * deltaTime);
    }