Search code examples
vue.jsvuexvuejs3composable

start useStore() from a composable after a click event


I have a simple Vue.js code where when a user is able to trigger commands from a BLE device. So i create a "useSession" composable, that connects to the IoT device, to the gateway, and to the server, and within each, push data to the store, mainly logs. To review the general structure, here's an example.

until now, I created and connected to the device using a custom useSession() composable function from within the setup() method in my main page component, and useStore() worked great within the composable functions.

Now my app is more complex, so I added a button that triggers the composable method. So when clicking the button, the session create all 3 connections (useSession().create())

The result is:

Cannot read properties of undefined (reading 'commit')

Looking into this issue i found this: https://forum.vuejs.org/t/is-it-possible-to-access-usestore-from-another-usexxx-composable/114230

You should be able to call useStore from within your composable, so long as the composable itself is called from within setup. The usual rules apply that it must be called during the synchronous execution of setup, not within an asynchronous callback.

To narrow down the problem, I suggest calling useStore within your setup function, immediately before you call useAuth, to confirm that it can access the store from there.

So it's clear to me why this isn't working, but not sure if passing down the store to each instance i'm connecting to is the best practice.


Solution

  • Composables are primarily supposed to be used when component instance is created - directly in setup function/block or respective options API (data and beforeCreate). Any other usage depends on their implementation and needs to be additionally confirmed.

    It's expected that useStore uses inject to propagate store value through component hierarchy, which is allowed only during setup.

    Since the use of a composable causes problem, it needs to be justified. There are only a few reasons to use it for global store. A composable allows to lazily access a store, which can be useful for resolving circular dependencies. provide/inject enables the use of dependency injection, it's possible for components of the project to get different default stores from root component, which is a corner case, e.g. for multi-app project.

    In any other cases Vuex/Pinia store can be directly imported where it's used. This requires to define it in a separate module.

    That useSession composable is required to be used apart from setup function/block suggests that it was incorrectly designed or used. Generally this case is handled like:

    <script setup>
    ...
    const session = useSession() // calls useStore
    
    const clickHandler = () => {
      session.create()
    }
    </script>