Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-rendering on clear #11

Open
changesbyjames opened this issue Feb 18, 2022 · 2 comments
Open

Re-rendering on clear #11

changesbyjames opened this issue Feb 18, 2022 · 2 comments

Comments

@changesbyjames
Copy link

Love the simplicity of the API, I'm in the process of converting all my hand rolled suspense helpers over to this!

One piece of functionality I'd love to have in the library or at least your opinion on how to implement it separately best is forcing a re-render when the cache is cleared/updated.

@ndrwksr
Copy link

ndrwksr commented Feb 18, 2022

I just did something similar yesterday, I am implementing a refresh feature and want to trigger a re-render when the refresh is done. Here is the solution I came up with:

// things.hooks.ts
const useRefreshing = () => useApp(R.prop('refreshing'))

export const useMyThings = (): ThinThing[] => {
  const fetchThings = useFetchThings()
  useRefreshing() // This causes this hook to execute and thereby rerender any time refreshing changes
  return suspend(fetchThings, fetchThingsKey)
}

export const useRefreshThings = (): AppState['refreshThings'] =>
  useApp(R.prop('refreshThings'))



// things.store.ts
// This is part of a Zustand store, another wonderful pmndrs project :)
export const createThingsSlice: StateSlice<ThingsSlice> = (set, get) => {
  ...

  const clearThings = () => clear(fetchThingsKey)

  const refreshThings = async () =>
    new Promise<void>(async (res) => {
      set({ refreshing: true })
      await get().refreshUser()
      clearThings()
      preload(
        () =>
          fetchThings().then((things) => {
            res()
            set({ refreshing: false })
            return things
          }),
        fetchThingsKey
      )
    })

  return {
    refreshing: false,
    fetchThings,
    refreshThings,
    clearThings,
  }
}

This approach doesn't quite accomplish what I want for refreshing (Suspense briefly renders the fallback), but it works great for re-rendering on clear. You could easily replace the store with useState, I have other reasons for using stores. The refreshThings function is a little ugly because I want to be able to await a call to refreshThings and have it resolve when the new data are available.

@ndrwksr
Copy link

ndrwksr commented Feb 18, 2022

A quick update... You can avoid the Suspend fallbacks flashing by changing set({ refreshing: false }) to setTimeout(() => {set({ refreshing: false })}, 0)

The flashing of the Suspend fallbacks occurs because calling set({refreshing: false}) before the promise returned by fn is resolved never gives suspend-react a chance to cache what the promise resolves to before set({refreshing: false}) queues a re-render with stale data. Then-ing a promise puts a job in the job queue (which is executed immediately) whereas setTimeoutputs a task in the task queue, which is not evaluated until the job queue is empty. So because query caches the result of the promise returned by fn in a then call, if the promise returned by fn is resolved before suspend is called again, the fresh data will be immediately available, and suspend will never throw a promise and causing a suspension. Using setTimeout to make the call to set({refreshing: false}) a task instead of a job will result in query finishing its execution before set({refreshing: false}) is called, so when a re-render is triggered, the fresh data have already been cached. At least, that's my best guess as to what's going on!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants