Skip to content

feat: createDataStore() hook#217

Open
rtritto wants to merge 9 commits intovikejs:mainfrom
rtritto:useData-to-useStoreWithData
Open

feat: createDataStore() hook#217
rtritto wants to merge 9 commits intovikejs:mainfrom
rtritto:useData-to-useStoreWithData

Conversation

@rtritto
Copy link
Copy Markdown
Contributor

@rtritto rtritto commented Feb 11, 2026

Rename useData to useStoreWithData.

Note: The return type of the hook was changed from Data to [Data, set<Data>].

Related

@brillout
Copy link
Copy Markdown
Member

Thanks for the PR! How about we keep useData as it is and just add useStoreWithData?

@rtritto rtritto changed the title refactor!: rename useData to useStoreWithData feat: add useStoreWithData Feb 11, 2026
@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 11, 2026

Done and updated the title: now it's a feature and not a breaking change (I think that's the intention)

@brillout
Copy link
Copy Markdown
Member

brillout commented Feb 11, 2026

Since it basically just wraps Solid's createStore, I wonder if createStoreWithData wouldn't be more fitting? Why does Solid call it createStore and not useStore?

But for React it would clearly be called useStoreWithData so it's a bit of a bummer if they end up being called differently.

Hm.

@rtritto rtritto force-pushed the useData-to-useStoreWithData branch from c12a349 to ce96c27 Compare February 11, 2026 20:13
@rtritto rtritto force-pushed the useData-to-useStoreWithData branch from ce96c27 to 1375088 Compare February 11, 2026 20:14
@brillout
Copy link
Copy Markdown
Member

Since we already useData, I'm inclined to go for useStoreWithData. We can later see if we want to rename use* to create*.

@rtritto @Blankeos WDYT?

@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 11, 2026

Since it basically just wraps Solid's createStore, I wonder if createStoreWithData wouldn't be more fitting?

You're right.

Why does Solid call it createStore and not useStore?

Because create* (eg createSignal) is the keyword for Solid like use* (eg useState) it's for React.

But for React it would clearly be called useStoreWithData so it's a bit of a bummer if they end up being called differently.

Other alternatives are to use From (store created from data) instead of With (store that includes data) or to remain with useData (generic/implicit, easy to remember inside vike because it's framework-agnostic).

IMO I will remain with useData

@magne4000 @phonzammi FYI and if you want to help/recommend/contribute

@brillout
Copy link
Copy Markdown
Member

IMO I still think useStateWithData is clearer. But it's a bummer we don't respect the Solid's create* naming convention.

In the meantime, we can go ahead an create a vike-react and vike-vue PR for creating useStateWithData. @rtritto Up for it?

@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 11, 2026

IMO I still think useStateWithData is clearer. But it's a bummer we don't respect the Solid's create* naming convention.

Or createData (not mentioned).

In the meantime, we can go ahead an create a vike-react and vike-vue PR for creating useStateWithData.

It's referred only to the renaming? If yes, I will wait a decision (maybe after other opinions) first at Solid side (this PR).
PS: I never used vike-react and vike-vue

@brillout
Copy link
Copy Markdown
Member

👍 Let's wait a bit and then proceed.

@brillout
Copy link
Copy Markdown
Member

Actually, thanks to @phonzammi's work on DocPress (most notably brillout/docpress#73 and brillout/docpress#91) naming it differently for React/Vue/Solid won't be that much of a problem for Vike's docs.

So I'd stay:

  • createStoreWithData for Solid
  • useStateWithData for React
  • Vue => not sure yet: useSignalWithData / createSignalWithData / useStateWithData? I'll ask AI.

@brillout
Copy link
Copy Markdown
Member

Claude suggests naming it useStateWithData for Vue — its argument is convincing.

So:

  • createStoreWithData for Solid
  • useStateWithData for React
  • useStateWithData also for Vue

Any objections?

@brillout
Copy link
Copy Markdown
Member

Actually... I think we can go with:

  • createStore for Solid
  • useState for React
  • useState for Vue

Because with TypeScript it becomes useState<Data>() which is very much explicit.

@rtritto I definitely agree with you that the more succinct the name of commonly used APIs the better (a lot easier to remember). Thank you for pushing on this! WDYT? Let's go with this?

@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 14, 2026

Actually... I think we can go with:

* `createStore` for Solid

* `useState` for React

* `useState` for Vue

Because with TypeScript it becomes useState<Data>() which is very much explicit.

@rtritto I definitely agree with you that the more succinct the name of commonly used APIs the better (a lot easier to remember). Thank you for pushing on this! WDYT? Let's go with this?

This naming conflicts with what exists in the framework, eg:

import { createStore } from 'solid-js/store'
import { createStore } from 'vike-solid/useData'
// Same naming

This is bad (it's not nice) because the user is forced to use an alias for 1 of the 2 and a search can find both.


IMO, I would use:

  • createData for Solid → createData<DataType>
  • useData for React → useData<DataType>
  • useData for Vue → useData<DataType>

Data refers that the value arrives from +data vike's hook.

So DataType is shared with +data:

// /pages/+data.ts
export const data = () => {
  ...
  return {
    ...
  } satisfies DataType
}

@brillout
Copy link
Copy Markdown
Member

brillout commented Feb 14, 2026

This is bad (it's not nice) because the user is forced to use an alias for 1 of the 2 and a search can find both.

That's a good point... you're right.

IMO, I would use:

  • createData for Solid → createData<DataType>
  • useData for React → useData<DataType>
  • useData for Vue → useData<DataType>

I think, often (most of the time?), the data is static after it's fetched (no client state) — there isn't any need to create a state. For this simple use case, the current useData implementation is very effective: it's simply const data = useData<Data>() — you cannot beat that simplicity.

That's why I'm inclined to define two different hooks:

  • useData => data (static)
  • useStateWithData => data + state

One could argue that useStateWithData can supersede useData. In other words, useStateWithData becomes useData (your proposal). But the DX for the aforementioned simple use case (static data) becomes: const [data] = useData<Data>(). That's less natural, and harder to remember (paper cut). At that point we might as well go for the useStateWithData naming (I agree the longer name is a paper cut).

Basically, we have to choose between two different paper cuts.

WDYT?

@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 14, 2026

That's why I'm inclined to define two different hooks:

* `useData` => data (static)

* `useStateWithData` => data + state

This is a design choice; the concept of multiple hooks is similar to jotai:

  • useAtomconst [value, setValue] = useAtom(anAtom)
  • useAtomValueconst value = useAtomValue(anAtom)
  • useSetAtomconst setValue = useSetAtom(anAtom)

There's a discussion for Jotai v3 and React v19, maybe can help to choose.

From my point of view, instead of using useStateWithData, it's clearer to use useStateFromData where state is produced from data: data is initially created in +data hook and then the state is created using (from) data.

The result will be:

  • createStoreFromData for Solid
  • useStateFromData for React
  • useStateFromData for Vue

In the end the design choice is between:

  1. One hook that returns: getter and setter
  • Pros: remember and use only 1 hook (only 1 code in the bundle)
  • Cons: if you need only getter, you have to destructure the hook with only the getter (less efficient)
  1. Two hooks that returns: getter + getter and setter
  • Pros: you have the specific hook for usage (efficient where you don't have to destructure the hook if you only need the getter)
  • Cons: remember and use the related hook by use case (you can have 2 code in the bundle, more space)

Both has pros and cons.

@brillout
Copy link
Copy Markdown
Member

From my point of view, instead of using useStateWithData, it's clearer to use useStateFromData where state is produced from data: data is initially created in +data hook and then the state is created using (from) data.

Makes sense!

The result will be:

  • createStoreFromData for Solid
  • useStateFromData for React
  • useStateFromData for Vue

Let me mull over it a bit 👀

@brillout
Copy link
Copy Markdown
Member

How about useDataState for React/Vue, and createDataStore for Solid? WDYT?

@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 15, 2026

Yes, it's better (compact and readable), LGTM
I will update this PR

@brillout
Copy link
Copy Markdown
Member

brillout commented Feb 15, 2026

💯I’m glad we found a sustainable name. Great constructive discussion.

@rtritto rtritto force-pushed the useData-to-useStoreWithData branch from ed26d30 to 6358ee2 Compare February 15, 2026 11:07
@rtritto rtritto force-pushed the useData-to-useStoreWithData branch from 6358ee2 to 46b892f Compare February 15, 2026 11:09
@rtritto rtritto changed the title feat: add useStoreWithData feat: add createDataStore Feb 15, 2026
@brillout
Copy link
Copy Markdown
Member

I wonder whether we can dedupe some code between createDataStore and useData 👀

@brillout
Copy link
Copy Markdown
Member

I wonder whether we can dedupe some code between createDataStore and useData 👀

It isn't much code, so not sure whether that's worth it.

@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 15, 2026

Do you mean in the examples?

- import { createDataStore } from "vike-solid/createDataStore";
+ import { useData } from "vike-solid/useData";

- const [movie] = createDataStore<Data>();
+ const movie = useData<Data>();

@brillout
Copy link
Copy Markdown
Member

I meant that the useData and createDataStore implementations are almost the same, so maybe it could be worth it to try to dedupe a bit 👀

@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 15, 2026

// useData.tsx
export { useData };

import { createDataStore } from "./createDataStore.jsx";

/** Access `pageContext.data` from any SolidJS component
 *
 * See
 * - https://vike.dev/data
 * - https://vike.dev/pageContext-anywhere
 */
function useData<Data>(): Data {
  const [data] = createDataStore<Data>();

  return data;
}

WDYT?

@brillout
Copy link
Copy Markdown
Member

LGTM. Also I'd say let's move the comment that refers #114 to useData(), and let's maybe improve the comment (I actually don't know why we must create a store for useData but maybe that's because I ain't familiar with Solid).

@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 15, 2026

LGTM. Also I'd say let's move the comment that refers #114 to useData(), and let's maybe improve the comment (I actually don't know why we must create a store for useData but maybe that's because I ain't familiar with Solid).

The implementation is inside createDataStore, so the comments for the issue #114 should be placed here.
I did the commit.
WDYT?

@brillout
Copy link
Copy Markdown
Member

0163ba5 — let me know if you disagree.

Let's create a PR for vike-{react,vue} then let's merge all 3 PRs together. (I'll take care of updating vike.dev/useData.)

@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 15, 2026

0163ba5 — let me know if you disagree.

I would only change like this:

- // We use a store to sync the store when the navigation (and thus `pageContext.data`) changes.
- // https://github.com/vikejs/vike-solid/issues/114
+ // https://github.com/vikejs/vike-solid/issues/114
+ // We use `createEffect` to sync the store when the navigation (and thus `pageContext.data`) changes.

@brillout
Copy link
Copy Markdown
Member

👍 Feel free to push

@brillout brillout changed the title feat: add createDataStore feat: createDataStore() hook Feb 16, 2026
@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 16, 2026

In Vue you should be able to set data directly by reference:

const data = useData()
// Update/set data
data.value.title = "New Title"
{{ data.title }}

So the useDataState isn't needed (correct me if I'm wrong, I never used Vue).


In the end, we have:

  • React: useData (get) + useDataState (get + set)
  • Solid: useData (get) + createDataStore (get + set)
  • Vue: useData (get + set)

Note: IMO, in Solid, the useData can be renamed to createData (isn't nice because the implementation use store but the naming doesn't contain Store) or (preferred) removed (the useData is only the gain less verbose code but the implementation/performance is slower than createDataStore) through deprecation.

@brillout WDYT?

@brillout
Copy link
Copy Markdown
Member

In Vue you should be able to set data directly by reference: [...] So the useDataState isn't needed (correct me if I'm wrong, I never used Vue).

Good point and I think you're right. (Although I also ain't that familiar with Vue. We should test this.)

Note that vike-vue current uses shallowReactive:

https://github.com/vikejs/vike-vue/blob/9b730a40798c2a8ccfc4e6ebf43c168608c080ec/packages/vike-vue/src/hooks/useData.ts#L16-L18

https://github.com/vikejs/vike-vue/blob/9b730a40798c2a8ccfc4e6ebf43c168608c080ec/packages/vike-vue/src/integration/createVueApp.ts#L73

https://github.com/vikejs/vike-vue/blob/9b730a40798c2a8ccfc4e6ebf43c168608c080ec/packages/vike-vue/src/integration/createVueApp.ts#L70

But we do plan to use shallowRef() instead:

So I guess merging that PR will do the trick for Vue?

Note: IMO, in Solid, the useData can be renamed to createData (isn't nice because the implementation use store but the naming doesn't contain Store) or (preferred) removed (the useData is only the gain less verbose code but the implementation/performance is slower than createDataStore) through deprecation.

See https://chatgpt.com/share/69934df1-d3c0-800d-8c83-0d383b2489bc and its response to my question Yea, this is the Solid implement. My point is that it's just a thin wrapper around createStore — I like ChatGPT's point here: naming should reflect behavior, not implementation details.

Concretely: isn't createDataStore() a convoluted name for the simple use case I mentioned above?

Wouldn't the user think "such a complex API for such a simple need: I just want to access data, why do I need to create a store just to access my data"?

(the useData is only the gain less verbose code but the implementation/performance is slower than createDataStore)

I don't see why useData would be slower than createDataStore.

@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 20, 2026

In Vue you should be able to set data directly by reference: [...] So the useDataState isn't needed (correct me if I'm wrong, I never used Vue).

Good point and I think you're right. (Although I also ain't that familiar with Vue. We should test this.)

Note that vike-vue current uses shallowReactive:

https://github.com/vikejs/vike-vue/blob/9b730a40798c2a8ccfc4e6ebf43c168608c080ec/packages/vike-vue/src/hooks/useData.ts#L16-L18

https://github.com/vikejs/vike-vue/blob/9b730a40798c2a8ccfc4e6ebf43c168608c080ec/packages/vike-vue/src/integration/createVueApp.ts#L73

https://github.com/vikejs/vike-vue/blob/9b730a40798c2a8ccfc4e6ebf43c168608c080ec/packages/vike-vue/src/integration/createVueApp.ts#L70

But we do plan to use shallowRef() instead:

* [198-use-shallowref-instead-of-shallowreactive-for-pagecontext-and-data (#198) vike-vue#215](https://github.com/vikejs/vike-vue/pull/215)

So I guess merging that PR will do the trick for Vue?

#215 will change behaviour useData from shallowref to shallowreactive.
To have useDataState we needs to use shallowref.
In the current implementation, the user can use useData as shallowref (imply that #215 should be a breaking change).
IMO #215, as one change/feature, can include both, the new behaviour of useData (shallowref changes to shallowreactive) and the new hook useDataState.
WDYT?

Note: IMO, in Solid, the useData can be renamed to createData (isn't nice because the implementation use store but the naming doesn't contain Store) or (preferred) removed (the useData is only the gain less verbose code but the implementation/performance is slower than createDataStore) through deprecation.

The main concept is provided by these points:

  • React uses use* and Solid uses create*
  • with only get, we use *Data
  • with get and set, we use *Data<Implementation>

As result (all combinations) we have:

  • React useData (get) and `useDataState (get + set)
  • Solid createData (get) and createDataStore (get + set)

That's why the get of Solid should be createData (Solid create* reference) instead of useData (React use* reference). Actually for Solid, this PR doesn't rename useData to createData.

isn't nice because the implementation use store but the naming doesn't contain Store

I agree. I was referring to the subtlety of comparing React hooks (useData, useDataState) with the Solid one, where useData implies no internal State. Anyway, NVM.

Concretely: isn't createDataStore() a convoluted name for the simple use case I mentioned above?

On Solid side, just create the minimum required hook with create* that has get and set; then choose by DX to use the optional hook of get or only the required one. Some clean names are createData and createDataStore as mentioned above.

Wouldn't the user think "such a complex API for such a simple need: I just want to access data, why do I need to create a store just to access my data"?

The Store is needed due to Solid's static behavior that's can be changed with createEffect.

If we want to omit the implementation, we could choose (like Jotai's naming):

  • React: useDataValue (get) - useData (get and set)
  • Solid: createDataValue (get) - createData (get and set)
  • Vue: useDataValue (get) - useData (get and set)

(the useData is only the gain less verbose code but the implementation/performance is slower than createDataStore)

I don't see why useData would be slower than createDataStore.

It's referred to dedupe: reuse the code (better maintainability) vs rewrite the code (raw code for performance). The performance is negligible, there's no need to pay attention to it.

@brillout
Copy link
Copy Markdown
Member

Vue

How about this?

See also: https://vuejs.org/guide/essentials/reactivity-fundamentals

Solid

Solid actually has useContext, so I think useData fits that naming.

I think createData is a weird name: conceptually, no data is being created. I'd argue that naming it createData is a leaky implementation detail (see the conversation I had with ChatGPT I shared earlier). The fact that we need a store is a Vike concern (the data store changes upon Vike navigation), rather than a user concern (the data store change isn't triggered by the user).

I don't think the users needs to know that useData() requires a store. (And I guess Solid users wouldn't necessarily expect it either, unless they actively think about it.)

On Solid side, just create the minimum required hook with create* that has get and set; then choose by DX to use the optional hook of get or only the required one.

I don't understand this sentence.

Sum up

  • React: useDataValue (get) - useData (get and set)
  • Solid: createDataValue (get) - createData (get and set)
  • Vue: useDataValue (get) - useData (get and set)

I don't see why you think this would be better. I think useData + useDataState is a lot more descriptive.

So, on my side, I'm voting for:

  • React: useData (get), useDataState (get and set)
  • Solid: useData (get), createDataStore (get and set)
  • Vue: useData (get), useDataReactive + useDataShallowReactive (get and set)

I like it. But I'm open to alternatives.

@rtritto
Copy link
Copy Markdown
Contributor Author

rtritto commented Feb 22, 2026

Vue

How about this?

* `useData` => `shallowRef()` (will be done at [198-use-shallowref-instead-of-shallowreactive-for-pagecontext-and-data (#198) vike-vue#215](https://github.com/vikejs/vike-vue/pull/215))

* `useDataReactive` => `reactive()` (deep)

* `useDataShallowReactive` => `shallowReactive()`

See also: https://vuejs.org/guide/essentials/reactivity-fundamentals

What about:

  • useDatashallowRef() (will be done at 198-use-shallowref-instead-of-shallowreactive-for-pagecontext-and-data (#198) vike-vue#215)
    E.g.:

    import { shallowRef } from 'vue'
    
    export function usePageDisplay() {
      const data = shallowRef({ show: { banner: false, alert: true } })
    
      function changePage(newData) {
        data.value = newData 
      }
    
      return { data, changePage }
    }
  • useDataRefref
    E.g.:

    import { ref } from 'vue'
    
    export function usePageInteraction() {
      const data = ref({ show: { banner: false, alert: true } })
    
      function changePage(newData) {
        data.value = newData
      }
    
      function toggleBanner() {
        // Note: the component with 'alert' will not be updated
        data.value.show.banner = !data.value.show.banner
      }
    
      return { data, changePage, toggleBanner }
    }

Maybe I'm wrong, WDYT?

Solid

Solid actually has useContext, so I think useData fits that naming.

Ok, so in Solid we will use useData (get) instead of createData (get).

I don't see why you think this would be better. I think useData + useDataState is a lot more descriptive.

Because it's simple: the *Data (get and set as default) with related implementation (it's not important, you need only to think about get and set); then, if you want only value, use the *DataValue.

@brillout
Copy link
Copy Markdown
Member

useDataRef

AFAICT it wouldn't add any added value compared to useDataReactive: it seems like ref() is the same as reactive() but with support for non-object valuespageContext.data is always expected to be an object though.

Because it's simple: the *Data (get and set as default) with related implementation (it's not important, you need only to think about get and set); then, if you want only value, use the *DataValue.

I'm inclined to think that:

  • Simple use case (only get) => simple name => useData
  • Advanced use case (get + set) => more verbose name => useDataState

If 80% (just a guess, I could be wrong) of the use cases are simple (only get), then that's also an argument to go for the the simplest name possible for the most common use case.

I think read-only +data (only get) is (a lot?) more frequent than read&write +data (get + set).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants