Suppose I need to fetch data with a POST request, where I have to send the ids in the request body.
const MyComponent = (props) => {
const body = useMemo( () => ({ ids: props.ids }), [])
const { data } = useSWR(['/api', body])
return //...
}
This works fine, the problem is that when I go to another page and then get back to the first one (containing MyComponent) the request is retriggered. This makes sense since useMemo only caches the data as long as the component is mounted. But is there a way to avoid retriggering the request when mounting the component again?
Thanks in advance,
You can set revalidateOnMount as false in the hook options. But ideally you use SWR only for GET requests, because the idea of SWR is that it will revalidate in many cases to ensure data consistency
Thanks for your answer @sergiodxa!!
I was wondering if there is no way to define the key as a fixed string (so SWR would know how to handle the cache and revalidation) and pass the body as a separate param that would not be compared.
I thinking about this because I have another use case where I am trying to update a chart with the data of the last 10 minutes.
In this case, my endpoint URL is always the same /chart-data but what is changing are the query params that I am sending /chart-data?from=123&to=456, where from and to should be the time range from the last 10 minutes.
I was reading that there is a way to implement a custom cache, but I don't know if that would be an overkill and I should not use SWR at all in this case.
Thanks again man!
The custom cache is not yet a thing, what I would do in your case is to prefetch the data before changing the key in SWR, this way once you change it is already there.
You can set
revalidateOnMountas false in the hook options. But ideally you use SWR only for GET requests, because the idea of SWR is that it will revalidate in many cases to ensure data consistency
with revalidateOnMount: false swr doesn't make request even when there is no cached data yet.
with revalidateOnMount: true request will be send even if cached date exists.
I think option revalidateOnCacheExists: false(or something like this) will be useful when we need to display cached date without any data requests.
What's the reason for not fetching data if it doesn't exist in cache when we set revalidateOnMount: false?
Further on this topic...
If I prefetch a key and then load a component with useSWR revalidateOnMount: true using the same key - the request ends up validating and never resolves.
I can fix this by pre-fetching the key and in the component have revalidateOnMount: false on the useSWR as you suggest...
But if I haven't pre-fetched the key, and the component is set to revalidateOnMount: false, then that component never loads the data!!!
And this only works when not using suspense, with suspense it ends up in a never ending re-rendering loop.
Why is this? @sergiodxa
and can you please clarify this in your article - https://sergiodxa.com/articles/next-swr-prefetch - as this is your tutorial I'm trying to follow actually
export const RenderAsYouFetch: FunctionComponent = () => {
const [id, setID] = useState<string>('')
const debouncedId = useDebounce<string>(id, 500)
async function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
const newID: string = event.target.value
setID(newID)
}
const [id2, setID2] = useState<string>('')
const debouncedId2 = useDebounce<string>(id2, 500)
async function handleChange2(event: React.ChangeEvent<HTMLInputElement>) {
const newID2: string = event.target.value
setID2(newID2)
}
// Fetch API (optional)
useEffect(() => {
if (debouncedId) {
getResource(debouncedId)
}
if (debouncedId2) {
getResource(debouncedId2)
}
}, [debouncedId, debouncedId2])
return (
<>
<label htmlFor="id">Resource ID:</label>{' '}
<input id="id" type="text" onChange={handleChange} value={id} />
<input id="id2" type="text" onChange={handleChange2} value={id2} />
{!debouncedId || !debouncedId2 ? (
<p>Enter IDs</p>
) : (
<>
<Resource id={debouncedId} />
<Resource id={debouncedId2} />
</>
)}
</>
)
}
export const Resource: React.FunctionComponent<IResourceProps> = ({ id }) => {
const { data } = useResource(id)
if (!data) return <Loading />
return <p>{data.url}</p>
}
import useSWR, { ConfigInterface, cache } from 'swr'
import { fetchAndCache, fetcher } from '../utils/swr.utils'
export const getResource = async (id: string) => {
if (!id) return
return fetchAndCache('resource/' + id)
}
export const useResource = (id: string, config: ConfigInterface = {}) => {
const currentData = cache.get(['resource', id])
return useSWR('resource/' + id, fetcher, { revalidateOnMount: false, ...config })
}
There is the deduplication option that helps to prevents unnecessary requests.
Most helpful comment
with
revalidateOnMount: falseswr doesn't make request even when there is no cached data yet.with
revalidateOnMount: truerequest will be send even if cached date exists.I think option
revalidateOnCacheExists: false(or something like this) will be useful when we need to display cached date without any data requests.