swr icon indicating copy to clipboard operation
swr copied to clipboard

UseSWR wrapper doesnt override and routes to the wrong fetcher (SWR global config's)

Open ridhwaans opened this issue 1 year ago • 2 comments

Bug report

Description / Observed Behavior

As the title says, I have a useSharedState hook that is used to pass state between components without prop drilling. This hook is not for http resources, it doesnt have a url fetcher but has an initial state option

import useSWR from 'swr'

export const useSharedState = (key, initial) => {
  const { data: state, mutate: setState } = useSWR(key, null, {
    fallbackData: initial,
  })

  return [state, setState]
}

I also have SWR global config in my App.js with its own fetcher

import useSWR, { SWRConfig } from 'swr'

const App = () => {

    return (
        <React.Fragment>
            <SWRConfig
                value={{
                    fetcher: (resource, init) => fetch(`${process.env.REACT_APP_BASE_URL}/api` + resource, init).then(res => res.json())
                }}
            >
                <BrowserRouter basename={process.env.PUBLIC_URL}>
                    <Routes>
                    ...

When I make an useSWR call it adds the base url to query endpoints in components

However when I have useSWR and useSharedState in the same component or when I call useSharedState directly, it goes to the wrong fetcher & url endpoint defined thats in SWRConfig

import useSWR from 'swr'
import { useSharedState } from "../../hooks/useSharedState"
...
function Books() {
    
    useEffect(() => {
        document.documentElement.className = "inset";
        document.body.className = "inset-body"; 
    }, [])

    const {data : recentlyAddedBooks } = useSWR('/books/recently_added');
    const [searchInput, setSearchInput] = useSharedState('searchInput') // used for getting text from a searchbox
    
    return (...)

it returns

`GET http://localhost:5000/apisearchInput 404 (Not Found)` because its not a resource url

Expected Behavior

Need all SWR calls with resource urls i.e. useSWR('/books') to follow the global fetcher in <SWRConfig/> but other SWR wrappers like useSharedState() that is not meant for API calls should not use global fetcher option

What am I doing wrong, do I need to fix the hook param to override fetcher?

Repro Steps / Code Example

Additional Context

swr version "^1.3.0",

ridhwaans avatar Sep 11 '22 16:09 ridhwaans

@ridhwaans Thank you for reporting this. A workaround for this is to pass fetcher: null to the useSWR in useSharedState. This avoids overriding useSWR's fetcher function by the global fetcher option.

import useSWR from 'swr'

export const useSharedState = (key, initial) => {
  const { data: state, mutate: setState } = useSWR(key, null, {
    fallbackData: initial,
    fetcher: null
  })

  return [state, setState]
}

It might be better to provide a better way for this if we'd like to push out this pattern.

koba04 avatar Sep 12 '22 04:09 koba04

A workaround for this is to pass fetcher: null to the useSWR.

I have the exact same issue as OP. I was trying to find workarounds myself, such as providing a noop operation () => {} to the second argument of useSWR or evaluate permanently to true for isPaused option. Some (sort of) worked but no solution I came up with was clean. Thank you for your workaround (although it is a bit verbose and not TS-friendly).

Dragate avatar Sep 12 '22 18:09 Dragate