reactjszustand

Zustand how to do call one function when every action execute?


My store as below

import { create } from "zustand";

type State = {
  name: string;
  age: number;
};

type Action = {
  updateName: (name: State['name']) => void;
  updateAge: (age: State['age']) => void
};

const initialState: State = {
  name: 'bob',
  age: 18,
};

export const useTempStore = create<State & Action>((set) => {
  return {
    ...initialState,
    updateName: (name) => set(() => ({ name })),
    updateAge: (age) => set(() => ({ age })),
  }
});

Now, I wanna persist store to server, so I changed the code as below

export const useTempStore = create<State & Action>((set) => {
  return {
    ...initialState,
    updateName: (name) => set(() => {
      request(name);
      return { name }
    }),
    updateAge: (age) => set(() => {
      request(age);
      return { age }
    }),
  }
});

const request = (data) => {
  // Send XHR to server
};

But if I have a lot of state, I will have to call request in every action. How can I make it call request when very action execute?


Solution

  • What you can do is to subscribe to store changes to fire side-effect.

    export const useTempStore = create<State & Action>((set) => {
      return {
        ...initialState,
        updateName: (name) => set(() => {
          return { name }
        }),
        updateAge: (age) => set(() => {
          return { age }
        }),
      }
    });
    
    useTempStore.subscribe((state) => {
       request(state);
    })
    
    const request = (data) => {
      // Send XHR to server
    };
    

    However in this case you will have to send the whole state of the store every time it changed.

    If you want to make requests with individual fields of the store, you will need to figure out somehow which field you want to update. In this case your original code is not bad at all. But if you want to decouple side effect logic from state actions you can use subscribe with selectors

    import { subscribeWithSelector } from 'zustand/middleware'
    
    
    export const useTempStore = create<State & Action>(subscribeWithSelector((set) => {
      return {
        ...initialState,
        updateName: (name) => set(() => {
          return { name }
        }),
        updateAge: (age) => set(() => {
          return { age }
        }),
      }
    }));
    
    // repeat for every field
    useTempStore.subscribe(
      state => state.name,
      (name) => {
        request(name);
      }
    );
    
    const request = (data) => {
      // Send XHR to server
    };
    

    It doesn't make the code shorter, but probably make it a bit cleaner.