swr icon indicating copy to clipboard operation
swr copied to clipboard

Provider used with conditional fetching causes problems

Open outloudvi opened this issue 2 years ago • 3 comments

Bug report

Description / Observed Behavior

Uncaught TypeError: can't access property 0, _a is undefined
    useSWRHandler index.mjs:634
    useSWRArgs index.mjs:612
    Article App.js:8
    React 9
    workLoop scheduler.development.js:266
    flushWork scheduler.development.js:239
    performWorkUntilDeadline scheduler.development.js:533

Expected Behavior

No error.

Repro Steps / Code Example

import useSWR, { SWRConfig } from 'swr'
import { useState, useEffect } from 'react'

const fetcher = (url) => fetch(url).then((r) => r.json())

function Article() {
  const [run, setRun] = useState(false)
  const { data } = useSWR(run ? 'https://httpbin.org/json' : null, fetcher)
  useEffect(() => {
    setRun(true)
  }, [])

  return (
    <h1>
      <div>{data?.slideshow?.author}</div>
    </h1>
  )
}

function App() {
  return (
    <SWRConfig
      value={{
        provider: () => new Map(),
      }}
    >
      <Article />
    </SWRConfig>
  )
}

export default App

Additional Context

SWR version: 1.3.0 Tested browser: Firefox 101.0a1 and Chromium 101.0.4951.41 on Arch Linux

The reason why I'm using SWR in this way is that I'm using it with Next.js, and I hope to configure a LocalStorage-based persistent cache. However, SWR seems to directly return the cached value on the client initial rendering. This makes the rendering different from server side rendering since the server doesn't necessarily has the same cache as the client, and finally spawns some hydration errors. A solution I've thought of is to let the client start loading data inside useEffect, which leads to this case.

outloudvi avatar May 01 '22 11:05 outloudvi

Looks like the custom cache has been recreated so that swr cannot access the original one. A workaround to get rid of this is keep same provider instance in each render. e.g. like using useRef

function App() {
  const m = useRef(new Map())
  return (
    <SWRConfig
      value={{
        provider: () => m.current,
      }}
    >
      <Article />
    </SWRConfig>
  )
}

huozhi avatar May 04 '22 14:05 huozhi

A workaround to get rid of this is keep same provider instance in each render.

Seems that it is working, thanks! Since I was basically just copying the documentation of SWR, I'm wondering had there been any update that break the sample code?

outloudvi avatar May 04 '22 16:05 outloudvi

Yeah sure, we'll update the docs later to present it with a better approach. Thanks!

huozhi avatar May 04 '22 16:05 huozhi