Tìm kiếm trang web

reactjs - Làm thế nào để tôi tạo một hook wrapper selector cho Zustand store trong Typescript


Tôi đang cố gắng sử dụng Zustand như một sự thay thế cho React Context để cung cấp một trạng thái được chia sẻ giữa một tập hợp các thành phần, và một trong những mẫu mà tôi thấy mình sử dụng nhiều là selector nông với nhiều thuộc tính, như sau:

// 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'

Theo hiểu biết của tôi, tôi cần sử dụng kiểm tra bằng shallow khi thực hiện kiểu selector nhiều thuộc tính này, nhưng rõ ràng đây là một mẫu rất dài dòng khi viết ra các khóa nhiều lần và luôn thêm tùy chọn shallow, đặc biệt nếu có nhiều hơn chỉ một hoặc hai khóa.

Tôi muốn tạo một hook bao bọc cho useStore chỉ nhận một danh sách các khóa và xử lý phần còn lại cho tôi, như sau:

// 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']);

Điều này hoạt động khá tốt (ít nhất là tôi nghĩ vậy - tôi không chắc liệu pick có làm hỏng các thứ về đăng ký selector hay không), nhưng tôi không thể làm cho kiểu dữ liệu hoạt động chính xác dù có cố gắng.

Các loại của Zustand khá phức tạp và thực hiện nhiều suy luận, nhưng có ai biết cách để thực hiện điều này không? Tôi có đang bỏ lỡ cách tiếp cận khác để giải quyết vấn đề này không?

Chúc sức khỏe!

Câu trả lời #1

Tôi sẽ đề xuất việc triển khai sau đây (có cú pháp hơi khác so với của bạn, nhưng cũng ít dài dòng hơn):

// 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>;
}

Sau đó, phần vỏ bọc:

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

Vậy cuối cùng, bạn có thể truy cập cửa hàng của mình bằng cách sau:

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

Bạn có thể xem ví dụ trực tiếp mà tôi đã làm nếu bạn muốn thử nghiệm