reactjs - Typescript'te bir Zustand store seçici hook sarmalayıcısını nasıl oluştururum


Zustand'ı, bir grup bileşenin arasında paylaşılan bir durumu sağlamak için React Context yerine kullanmaya çalışıyorum ve sık sık kullandığım kalıplardan biri, şöyle bir sığ çok özellikli seçici:

// store.ts
interface State {
  data: object,
  setData: (newData: State['data']) => void,
}

const useStore = create<State>(set => ({
  data: {},
  setData: (data) => set({ data }),
});


// Component.tsx
import shallow from 'zustand/shallow';
import useStore from './store.ts';

const { data, setData } = useStore(
  ({ data, setData }) => ({ data, setData }),
  shallow
);

// Use 'data' and 'setData'

Benim anladığım kadarıyla, bu tür çok özellikli seçim işleminde shallow eşitlik kontrolünü kullanmam gerekiyor, ama bu açıkça anahtarları birkaç kez yazıp her zaman shallow seçeneğini eklemek oldukça ayrıntılı bir desen, özellikle bir veya iki anahtardan daha fazlaysa.

useStore için, sadece bir anahtar listesi alacak ve geri kalanını benim için halledecek bir sarmalayıcı hook oluşturmak istiyorum, şöyle:

// store.ts
import pick from 'lodash/pick';

const useStoreState = (keys: (keyof State)[]) =>
  useStore(state => pick(state, keys), shallow)

// Component.tsx
import { useStoreState } from './store';

// Ideally the return value here has the same type as the example above
const { data, setData } = useStoreState(['data', 'setData']);

Bu gayet iyi çalışıyor (en azından benim düşündüğüm kadarıyla - pick'in seçici abonelik işlerini bozup bozmadığını kesin olarak bilmiyorum), ama hayatım boyunca tiplerin doğru çalışmasını bir türlü sağlayamıyorum.

Zustand'ın türleri oldukça karmaşık ve birçok çıkarım yapıyor, ama bunu başarmanın bir yolunu bilen var mı? Bu sorunu ele almak için başka bir yaklaşımı mı kaçırıyorum?

Şerefe!

Cevap #1

Şu önerilen uygulamayı sunmak isterim (sizinkinin biraz farklı bir sözdizimi var, ama aynı zamanda daha az ayrıntılı):

// Utility function, to allow us to pick fields from the store
const useMulti = <T extends object, K extends keyof T>(
  useStoreFn: UseBoundStore<StoreApi<T>>,
  ...items: K[]
): Pick<T, K> => {
  return items.reduce(
    (carry, item) => ({
      ...carry,
      // No need to use useShallow here, as we fetch the objects one by one
      [item]: useStoreFn((state) => state[item]),
    }),
    {}
  ) as Pick<T, K>;
}

Sonra, sargı:

// Wrapper store hook
const useStoreState = (...items: Array<keyof State>) =>
  useMulti(useStore, ...items)

Yani sonunda, mağazanıza şu şekilde erişebilirsiniz:

const {data,setData} = useStoreState("data","setData")

Oynamak isterseniz yaptığım bu canlı örneği kontrol edebilirsiniz