#Error when trying to use provider method

1 messages · Page 1 of 1 (latest)

wind lily
#

I have the following method setup in my /src/quickPlay/provider.tsx file:

const stopAllSounds = () => {
    store.sounds.forEach((sound, index) => {
        setStore("sounds", index, "status", "stopped")
        stopSound(sound.id)
    })
}```

The providers export looks like this:

```ts
export const [QuickSoundProvider, useStore] = createContextProvider(() => {
    return {
        store: store,
        setStore: setStore,
        addSound: () => addSound(),
        removeSound: (soundId) => removeSound(soundId),
        playSound: (soundId) => playSound(soundId),
        stopAllSounds: () => stopAllSounds(),
        setSoundVolume: (soundId, event) => setSoundVolume(soundId, event),
        setSearch: (value) => setSearch(value),
        getSounds: () => getSounds()
    }
})```
#

I import this into my main layout file /src/layout.tsx and try to use it in the beforeunload event:

import { QuickPlay } from './quickPlay/view'
import { Scene } from './scene/scene'
import { QuickSoundProvider, useStore } from './quickPlay/provider'
import svgIconUrl from '../assets/icon.svg'
import { onMount } from 'solid-js'

export function Layout() {

    onMount(() => {
        const splashScreen = document.getElementsByClassName('splash')[0]

        setTimeout(() => {
            splashScreen.classList.add('fade-out')
            setTimeout(() => {
                splashScreen.classList.add('d-none')
            }, 2000)
        }, 2000)

        // window.electronAPI.onAppBeforeQuit(() => {
        //     alert('test');
        //     console.log('Application is about to quit.')
        //     const { stopAllSounds } = useStore()
        //     stopAllSounds()
        // })

        window.addEventListener('beforeunload', (event) => {
            event.preventDefault()
            const { stopAllSounds } = useStore() // ! errors with TypeError: Cannot destructure property 'stopAllSounds' of 'useStore(...)' as it is undefined.
            stopAllSounds()
            console.log('I do not want to be closed')
          
            // Unlike usual browsers that a message box will be prompted to users, returning
            // a non-void value will silently cancel the close.
            // It is recommended to use the dialog API to let the user confirm closing the
            // application.
            e.returnValue = true
        })
    })```

I get this error when closing the window:

TypeError: Cannot destructure property 'stopAllSounds' of 'useStore(...)' as it is undefined.
at layout.tsx:28:21```

pearl barn
#

Doing useContext in an event handler is not going to work

#

Context only works during mount

#

A but it has default values 🤔

#

Is it the intention to just use it as a singleton? Like you never override its default values?

wind lily
#

No the store changes alot

#

For example when it's playing the sound the specifics sounds status property is updated to playing

#

@pearl barn I am attempting to set all the sound objects status property to stopped when the window is closed so when it's loaded next it won't think the sounds are playing when they are actually stopped

pearl barn
#

a my bad

#

i was misinterpreting how createContextProvider works

#

you can not get context in event handlers

#

that's one of the limitations of context: it does not work behind any async barrier

#
createEffect(async () => {
   await new Promise((resolve) => setTimeout(resolve, 1000));
   const context = useContext()
})
``` this will not work for example
#

event handlers are similar: they run after the component is called

pearl barn
wind lily
#

Interesting, I'll take a look at that example playground

pearl barn
#

Can mb give an intuition

wind lily
#

that makes sense it has been long enough that I forgot those were even context providers. I see that in your example it logs out of order second then first.

#

does that mean there is no way to adjust the store beforeunload?

#

specifically this provider

pearl barn
#

Access the context before the async boundary

wind lily
#

@pearl barn I just gave that a try with no luck but im assuming it may be because im outside the provider

pearl barn
#

The way how you are doing context is a bit unorthodox btw

#

Are you sure you don't want to do a singleton instead?

wind lily
#

im adjusting now to do it inside the context

#

i mean ideally there should only ever be one of those providers

pearl barn
#

Do you want to do ssr?

#

Because how you are handling context rn will still create issues if you would ssr it

wind lily
#

in this case probably not as this is an electron app wrapped around solid js

#

and my understanding is electron just runs a chrome browser

#

so moving that code into the view which is inside the provider fixes the error

#

now i just need to figure out why its not doing what its supposed to

pearl barn
#

With context the goal is often to scope things to a branch of the ui-tree

pearl barn
wind lily
#

yea and there is ui that goes along with this. in this case there will only ever be one provider in the tree though

#

is there anything specifically wrong with how the provider is setup? im always game for learning

pearl barn
# wind lily is there anything specifically wrong with how the provider is setup? im always g...

What makes your setup a bit unorthodox is that it is something in between a singleton and context. It's like you are providing a singleton, but through context. So it's a bit the worst of the two worlds: you cannot ssr it (which doesn't matter in ur case) and you have to think about where you access the store/actions: not all useContexts will work, as we saw in the example of the event handler.

#

I m personally a big fan of the singleton approach, and would only introduce the added complexity of context if I would really need it: for example if you would want to have multiple instances of the store/actions in a single page, or if u wanna ssr it.

wind lily
#

yea i have no issues with a singleton, the docs just specifically mention using the context providers to handle this. The singleton im assuming would be more vanilla js than solidjs correct? less magic

#

i guess the only other question too is reactivity on the singleton

pearl barn
#

The singleton is having a store somewhere and mb some functions to manipulate them and then you just import these functions and this store wherever you want to

#

Sorry if that wasn't clear, is bit jargony

wind lily
#

all good, does anything that ever references a store get updated by the redering?

pearl barn
wind lily
#

ok then yea maybe a simple example of the singleton store would help

#

let me see if i can find it in the docs

pearl barn
pearl barn
# wind lily i think maybe i saw this page https://www.solidjs.com/tutorial/stores_context an...

What they mean with Using Context has the benefit of being created as part of the reactive system and managed by it. is that solid automatically cleans up effects and signals that are created inside a component whenever the component unmounts.

With singletons you often don't need to clean up: you want this store to be available the whole time your app is running. You might sometimes get a warning computations outside of createRoot cannot be cleaned up which refers to that.

#

You can safely ignore those, or wrap the store and actions with a createRoot if you don't like ignoring warnings

#

In your case the store and the actions were still created outside of components, so you might have seen that warning too.

wind lily
#

ok ill mess around and see if i can come up with a singleton store

wind lily
#

@pearl barn finally got some time to mess around with this again. This what you were thinking?


class StoreSingleton {
  // Private static instance
  static #instance;

  // Private constructor to prevent direct instantiation
  constructor() {
    if (StoreSingleton.#instance) {
      throw new Error(
        "Use StoreSingleton.getInstance() to get an instance of this class.",
      );
    }

    // Initialize the Solid.js store with default state
    this.state = createStore({
      user: null,
      todos: [],
    });
  }

  // Public static method to get the singleton instance
  static getInstance() {
    if (!StoreSingleton.#instance) {
      StoreSingleton.#instance = new StoreSingleton();
    }
    return StoreSingleton.#instance;
  }

  // Getter for accessing the Solid store
  getState() {
    return this.state[0];
  }

  // Methods to update the state
  setUser(user) {
    this.state[1]({ user });
  }

  addTodo(todo) {
    this.state[1]("todos", (todos) => [...todos, todo]);
  }

  removeTodo(index) {
    this.state[1]("todos", (todos) => todos.filter((_, i) => i !== index));
  }
}

// Usage
const store = StoreSingleton.getInstance();
store.setUser({ id: 1, name: "John Doe" });
store.addTodo({ id: 1, text: "Learn Solid.js", completed: false });
store.addTodo({ id: 2, text: "Build a project", completed: false });

console.log(store.getState());
// Output:
// {
//   user: { id: 1, name: "John Doe" },
//   todos: [
//     { id: 1, text: "Learn Solid.js", completed: false },
//     { id: 2, text: "Build a project", completed: false }
//   ]
// }

const sameStore = StoreSingleton.getInstance();
sameStore.removeTodo(0);

console.log(sameStore.getState());
// Output:
// {
//   user: { id: 1, name: "John Doe" },
//   todos: [
//     { id: 2, text: "Build a project", completed: false }
//   ]
// }```
#

Obviously not specific to my setup just yet but wanted to make sure thats the general idea before i modify my code base

#

also wanted to get some ideas on where you might keep such a file directory structure wise

pearl barn
wind lily
#

gotch but without the constructor wont the import always get a new instance ?

pearl barn
#

no! that's the beauty of modules

#

you can scope variables that are used throughout your project just with files, you don't need additional abstractions.

#

for example

// main.js
import "./module1"
import "./module2"

// module1.js
console.log('hallo')

// module2.js
import "./module2"

is only going to log 'hallo' once: that's because the body of a module is only called once: the first time it is imported from somewhere.

wind lily
#

very interesting, im used to php where you have to do some work to make something a signleton. Thats really good to know and much simpler than i figured it would be. Now I will start messing around with them in my app

wind lily