feat(web): Add createSelectors utility for Zustand stores #21717

Closed
opened 2026-02-21 20:14:00 -05:00 by yindo · 1 comment
Owner

Originally created by @lyzno1 on GitHub (Jan 16, 2026).

Current Pattern

https://github.com/langgenius/dify/blob/fad6fa141d0b0fca09075bdd420a21564f4eb3a9/web/app/components/workflow/header/global-variable-button.tsx#L11-L15

Each selector requires writing s => s.xxx:

const showGlobalVariablePanel = useStore(s => s.showGlobalVariablePanel)
const setShowGlobalVariablePanel = useStore(s => s.setShowGlobalVariablePanel)

Alternative: Auto-generated Selectors

Based on Zustand docs:

const showGlobalVariablePanel = workflowStore.use.showGlobalVariablePanel()
const setShowGlobalVariablePanel = workflowStore.use.setShowGlobalVariablePanel()

DX benefit: IDE autocomplete shows all 90+ store fields after typing workflowStore.use.

Utility

import type { StoreApi, UseBoundStore } from 'zustand'

type StoreSelectors<T> = {
  [K in keyof T]: () => T[K]
}

export type StoreWithSelectors<T> = {
  use: StoreSelectors<T>
  useStore: <U>(selector: (state: T) => U) => U
}

export function createSelectors<T extends object>(
  useStoreHook: <U>(selector: (state: T) => U) => U,
): StoreWithSelectors<T> {
  const store = {} as StoreWithSelectors<T>
  store.useStore = useStoreHook
  store.use = new Proxy({} as StoreSelectors<T>, {
    get(_target, prop: string | symbol) {
      if (typeof prop !== 'string') return undefined
      return () => useStoreHook(state => state[prop as keyof T])
    },
  })
  return store
}
Originally created by @lyzno1 on GitHub (Jan 16, 2026). ## Current Pattern https://github.com/langgenius/dify/blob/fad6fa141d0b0fca09075bdd420a21564f4eb3a9/web/app/components/workflow/header/global-variable-button.tsx#L11-L15 Each selector requires writing `s => s.xxx`: ```typescript const showGlobalVariablePanel = useStore(s => s.showGlobalVariablePanel) const setShowGlobalVariablePanel = useStore(s => s.setShowGlobalVariablePanel) ``` ## Alternative: Auto-generated Selectors Based on [Zustand docs](https://zustand.docs.pmnd.rs/guides/auto-generating-selectors): ```typescript const showGlobalVariablePanel = workflowStore.use.showGlobalVariablePanel() const setShowGlobalVariablePanel = workflowStore.use.setShowGlobalVariablePanel() ``` **DX benefit**: IDE autocomplete shows all 90+ store fields after typing `workflowStore.use.` ## Utility ```typescript import type { StoreApi, UseBoundStore } from 'zustand' type StoreSelectors<T> = { [K in keyof T]: () => T[K] } export type StoreWithSelectors<T> = { use: StoreSelectors<T> useStore: <U>(selector: (state: T) => U) => U } export function createSelectors<T extends object>( useStoreHook: <U>(selector: (state: T) => U) => U, ): StoreWithSelectors<T> { const store = {} as StoreWithSelectors<T> store.useStore = useStoreHook store.use = new Proxy({} as StoreSelectors<T>, { get(_target, prop: string | symbol) { if (typeof prop !== 'string') return undefined return () => useStoreHook(state => state[prop as keyof T]) }, }) return store } ```
yindo added the 💪 enhancement label 2026-02-21 20:14:00 -05:00
yindo closed this issue 2026-02-21 20:14:01 -05:00
Author
Owner

@lyzno1 commented on GitHub (Jan 16, 2026):

Not very necessary.

@lyzno1 commented on GitHub (Jan 16, 2026): Not very necessary.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify#21717