reactjszustand

I need to call a hook inside a zustand action


I need to show a toast_message if the user select a large number of elements in one table. The toast message function is a return of a custom hook, but if i call a Hook inside the zustand the aplication gives me the error "Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app".

I can't show the code, because is a enterprise code.

But my question is, does hae the posibility to call a hook inside the zustand create or set ?

I tried to call a hook inside the create, but before the return of the store state.

For example in this below code,but in my code is an custom hook and not a useState.

export const storeExample = create<{
  count: number;
  increaseCount: () => void;
}>()((set) => {
  const someState = useState("");
  return {
    count: 0,
    increaseCount: () => set((state) => ({ count: state.count + 1 })),
  };
});

Solution

  • You can't directly. According to this, you can use zustand even without react and according to this discussion in the zustand-Repository, one of the Maintainers explains that:

    Store definition is pure JS. You want to create a new hook from the store hook.

    This means that you cannot use Hooks in your reducers.

    He gave an example on how you could work around this:

    const useCustomFetch = (path) => { ... }
    
    const useFishStore = create((set) => ({
      fishies: {},
      setFishes: (fishes) => set({ fishes }),
    }))
    
    const useFishFetch = () => {
      const customFetch = useCustomFetch()
      const setFishes = useFishStore((state) => state.setFishes)
      return async (pond) => {
        const response = await customFetch(pond)
        set({ fishies: await response.json() })
      }
    }
    

    There's also an example how you can feed customFetch into the store action, but still using a separate Hook outside of your store. In this case, you'd need to be aware that this action relies on customFetch to be fed as arg from outside which ultimately means it cannot be called directly.

    const useCustomFetch = (path) => { ... }
    
    const useFishStore = create((set) => ({
      fishies: {},
      fetch: async (pond, customFetch) => {
        const response = await customFetch(pond)
        set({ fishies: await response.json() })
      },
    }))
    
    const useFishFetch = () => {
      const customFetch = useCustomFetch()
      return (pond) => useFishStore.getState().fetch(pond, customFetch)
    }