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

Request for MapWritableState nested object access. #634

Open
davidboom95 opened this issue Aug 28, 2021 · 9 comments
Open

Request for MapWritableState nested object access. #634

davidboom95 opened this issue Aug 28, 2021 · 9 comments

Comments

@davidboom95
Copy link

What problem is this solving

Modifying deeply nested properties in the state.

Proposed solution

// Object
...mapWritableState(useStore, {
     myCity: 'form.address.city',
})

// Array ( outputs the latest key as the variable name, in this case city)
...mapWritableState(useStore, ['form.address.city'])
@posva
Copy link
Member

posva commented Aug 30, 2021

I'm personally not a fan of this syntax unless it manages to:

  • fully handle TS (autocompletion)
  • not increase the size of mapWritableState() much
  • Find a way to handle edge cases around names

Another alternative that could work in TS but is way more verbose and IMO not intuitive would be supplying a function and a key

mapWritableState(useStore, { city: [state => state.adress, 'city'] })
mapWritableState(useStore, { city: { getter: state => state.adress, key: 'city'] }})

@davidboom95
Copy link
Author

How about ?

mapWritableState(useStore, ['form', 'address', 'city'])

@posva
Copy link
Member

posva commented Oct 6, 2021

that syntax would be confusing with the existing one mapState(useStore, ['name', 'email']) that maps 2 properties.

@posva
Copy link
Member

posva commented Nov 5, 2021

This should be doable with this TS type:

export type ObjectPaths<
  O extends Record<string, any>,
  Path extends string = ``
> = keyof O extends string
  ?
      | `${Path}${keyof O}`
      | _ObjectValues<{
          [K in keyof O as O[K] extends Record<string, any>
            ? K extends string
              ? K
              : never
            : never]: ObjectPaths<O[K], `${Path}${K}.`>
        }>
  : never

type _ObjectValues<T> = T extends Record<string, infer R>
  ? Extract<R, string>
  : never

const nestedObj = {
  a: 'hey',
  b: { a: 'hey', b: 'hello', c: { d: 'hey', e: 'f' } },
  nested: { a: { b: { c: { d: 'e' } } } },
}

function acceptKeys<T>(o: T, keys: ObjectPaths<T>[]) {}
acceptKeys(nestedObj, ['a', 'b', 'b.a', 'no', ''])

Feel free to pick it up from here if you want. It's worth noting that right now we accept property keys with a . in them so maybe we still have to find a way to allow both behaviours but I'm not sure yet.

@cefn
Copy link

cefn commented Feb 12, 2022

An alternative might be to create partitioned 'child' stores from a parent store. Then the addressing of any particular entry would be 'top level' again.

This could even potentially be done within a setup function (and have the dynamic binding change as the values bound from setup changed reactively).

Syntax for OP's case would end up like...

const useFormStore = partitionStore(useStore. 'form');
const useAddressStore = partitionStore(useFormStore. 'address');
return {
  ...mapWritableState(useAddressStore, ["city"])
}

...which could potentially be accelerated by a path-oriented syntax...

return {
  ...mapWritableState(partitionStore(useStore, ["form", "address"]), "city")
}

It also has the beneficial side-effect of being able to author form items that do not know whether the item they are bound to is the WHOLE store, or just a sub-part, since they just address their binding relative to the partitioned store. This potentially makes it easier to refactor stores.

@aldencolerain
Copy link

I also think this would be very helpful. I'm not using typescript or keys with . so it was easy to write my own functions to do this for my application. However, I would love to see this idea merged in at some point.

In the past (Vue2) we used vuex-pathify and they had some nice helper functions get and sync to do this. A lot of the problems solved by vuex-pathify are non existent in Pinia, but the mapping functions were very helpful for those using the Options API. To me though, the nested keys were cleaner when mapped individually. Although it sounds like adding additional functions may be a non-starter.

computed: {
  id: get(store, 'profile.id'),
  name: sync(store, 'profile.name'),
},

In regard to . being allowed in keys, perhaps a third argument indicating its nested (and dots will be interpreted in that way):

mapWritableState(useStore, {
 foo: 'a[0].b.c'
}, true) //  nested = true

Or a helper function that generates a function:

mapWritableState(useStore, {
 foo: nested('a[0].b.c')
})

I'm don't use TS autocomplete etc, so not sure if those ideas would translate.

@ferrykranenburgcw
Copy link

We have lots of code where we pass the full path to the state and use only the last 'key' as the local name. Would love to see this working in Pinia, because it is the only thing holding us back of migrating from Vuex -> Pinia.

@Bassadin
Copy link

Bassadin commented Aug 2, 2022

Is there any update to this? We'd also love to be able to do this or just pass a function like with mapState(). :)

@STachiR20
Copy link

+1, this would be great to have

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants