zustand - infinite re-render when using many async functions with the same loading state

281 views Asked by At

I'm using zustand 4.1.5 in my react app.

When user hits the LogDetails tab, two async functions which have same loading state setting inside hit concurrently, and somehow it causes an infinite rerender and <ComponentSpinner /> in index.js file spinning permanently. When I check the loading state in index.js file, I see it is false.

here is zustand store that I use:

const initialState = { 
  selectedFair: {}, 
  params: {}, 
  fairLogDetails: {}, 
  loading: false, 
  error: null 
}
    
const useFairStore = create((set, get) => ({
  ...initialState,   
  getFairs: async (params) => { 
    const endpoint = params?.role === 'admin' ? '/fair/admin' : '/fair/all' 
    set({ loading: true }) 
    try { 
      const { data } = await axios( ${endpoint}?page=${params?.page || 1}&limit=${params?.limit || 10} ) 
      set({ fairs: data?.data, totalFairs: data?.total, params, error: null }) 
    } catch (e) { 
      set({ error: e }) 
    } finally { 
      set({ loading: false }) 
    } 
  },
  getFairLogDetails: async (params, reqBody = {}) => { 
    set({ loading: true })
    try {
      const { fairId } = params
      const response = await axios.post(`/report/fair-statistics/${fairId}`, reqBody)
      set({
        fairLogDetails: response?.data,
        error: null
      })
    } catch (e) {
      set({ error: e })
      return Promise.reject({ 
        code: e?.response?.status, 
        message: e?.response?.data?.message 
      })
    } finally {
      set({ loading: false })
    }
  },
}

I call this functions on fairDetails page. in the index.js component:

const { fairId } = useParams()
const [selectedFair, getFair, loading, cleanStore] = useFairStore( (state) => [state.selectedFair, state.getFair, state.loading, state.cleanStore], shallow)
    
useEffect(() => { 
  getFair(fairId)
  return () => cleanStore()
}, [fairId])

...
    
if (loading) return <ComponentSpinner />
    
return ...
    
}

in my nested LogDetails.js component:

const [selectedFair, getFairLogDetails, fairLogDetails] = useFairStore( (state) => [state.selectedFair, state.getFairLogDetails, state.fairLogDetails], shallow )

useEffect(() => {
  getFairLogDetails({fairId: selectedFair?._id})
}, [selectedFair?._id])
 
return ...

So, when user hits the LogDetails tab, two async functions hit concurrently, and somehow it causes an infinite rerender and <ComponentSpinner /> in index.js file spinning permanently. When I check the loading state in index.js file, I see it is false. I'll be very appreciated if someone can explain the reason of this infinite rerender and how should I change this logic, thanks in advance.

1

There are 1 answers

0
flq On BEST ANSWER

Since you either render details or spinner on pending, the moment the child wants to fetch something, the detail will be unmounted since you now render the spinner.

Then in the background zustand is actually still fetching and ending the pending state. Now you show the detail again, but since the child is now being remounted, you trigger once more the fetching of the detail, ad-infinitum.