davestewart / vue-class-store

Universal Vue stores you write once and use anywhere
https://github.com/davestewart/vue-class-store-demos
MIT License
276 stars 13 forks source link

Composition API discussion #12

Open Mootook opened 3 years ago

Mootook commented 3 years ago

Just replying to https://github.com/davestewart/vue-class-store/issues/11#issuecomment-726939231 As I'd like to discuss more about vue-class-store.

I agree about computed properties being a nice workflow, and it's what I've grown used to, but I'm wondering your thoughts on how vue-class-store can work with the composition api.

From my understanding this package is best used as a provider at the top level then subsequently injected where needed. I've setup a codesandbox to try and work through some possible setups. I assume much of the same principles carry along with modifying the store.

Please let me know if you have thoughts on best ways to inject specific store properties at the child level. Thanks!

davestewart commented 3 years ago

For sure!

So, caveat: I've not done that much with the Composition API as yet.

Just checking your sandbox and the API docs, so that's the Composition API's provide in action, huh?

Good to see it just working!

The demos have example of a global inject in each of them, though that is using the Options API for Vue 3:

There is a working sandbox here:

As mentioned I'm working on some more global stuff as soon as I get time, but it's more Vue 2 based – as that's what all my projects are right now – but I'll try to get some more Vue 3 under my belt, and feel free to keep dropping your findings here :)

djmaze commented 3 years ago

@Mootook Really cool and obvious approach by using useStore for injection. I like it. We did it a bit more complicated in our project, so I took the opportunity to clean it up now.

As we have multiple stores in our app (and we also use typescript), I built a generic wrapper module for injecting stores. You can find it in this sandbox. It would be cool to be able to get rid of the separate useStore() and injectStore() though somehow. (EDIT: I refactored it so the store module now exports the provide / inject methods itself, like in Mootook's example.)

Mootook commented 3 years ago

Curious about potential overhead with injecting across the app. From the docs:

provide and inject are primarily provided for advanced plugin / component library use cases. It is NOT recommended to use them in generic application code.

Anyone have any insight on the comparison between directly injecting the store vs passing it via props? Injecting seems fine, recommended even, for grandchild components, but since this is functioning like a pseudo-global store, wondering if the concepts still hold for every level of the component hierarchy.

davestewart commented 3 years ago

provide and inject are primarily provided for advanced plugin / component library use cases. It is NOT recommended to use them in generic application code.

This is increasingly taken to be fairly Vue 2-opinionated, and perhaps not even accurate for Vue 2.

My impression was at the time, that was what the feature was intended for, and the developers were more keen to shepherd people towards Vuex, but the last few years have been a learning experience, and provide/inject now compares favourably in terms of outcome/overhead.

TL;DR not so relevant now

davestewart commented 3 years ago

FWIW Element UI does a lot of injecting for its form elements. Some of it is really rather clever:

Mootook commented 3 years ago

Ah, okay. Makes sense. Thank you.

Mootook commented 3 years ago

As for the global state, what are your thoughts on creating a static property on the root of the store which can be accessed by child classes? I'm mainly looking for a way to communicate across nested objects/classes within the store and not necessarily have to chain things together in some component...codesandbox has some issues disallowing eslint which makes a demo difficult but here's the rough idea:

class Store {
  static instance
  constructor () {
    this.nestedClass = new NestedClass()
  }

  bar () {
    console.log('Called from a nested class')
  }
}

class NestedClass {
  foo () {
    Store.instance.bar() // Called from a nested class
  }
}

const store = new Store()
Store.instance = store
store.nestedClass.foo()

This small test case "works", I just want to make sure it's not going against what the utility of this library.

davestewart commented 3 years ago

Yeah, it's totally up to you.

I've been pondering various ideas, some more registerStore() type helpers with a global state POJO (as the top level may not need to be reactive).

Or maybe (because the decorator is a factory function) this.$store or this.$root is injected into each instance.

That would likely need some additional augmentation of the VueStore type, which isn't a problem, would just need to talk to people and find out what feels like the best option; maybe it would be best left to users to augment their own stores:

I know there are some situations where TypeScript and static types don't play so well together (I found generics don't support it) but how did you get on? Did the compiler mind, or did you need to jump through hoops?

class Store {
  static instance: Store

  constructor () {
    Store.instance = this // could just do it here
    this.nestedClass = new NestedClass()
  }
}
Mootook commented 3 years ago

I don't use TypeScript in my project, and the only issue I had was defining properties outside the constructor. Also, Store.instance = this, I found to have issues with reactivity, since I'm not sure if it's a vue instance at that point in the constructor, since I'm calling it with VueStore.create(new Store())

davestewart commented 3 years ago

OK, good info, thanks.