-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
useSWR to fetch only one time #450
Comments
@oran1248 If you are planning on fetching data strictly once, then why would you care about cache at all? At any event, you could turn off all refreshing and revalidation using: useSWR(key, {
revalidateOnFocus: false,
revalidateOnMount:false,
revalidateOnReconnect: false,
refreshWhenOffline: false,
refreshWhenHidden: false,
refreshInterval: 0
}); Note that some of the above are actually the default values. |
Because |
@oran1248 You said you want to "fetch data only once" - but then talk about the benefits of "revalidation" and "cache". Those two concepts imply, and are strictly tied to, fetching multiple times. I may have been thrown off the scent here. I'm sorry if that's the case. |
@nfantone What I mean is "fetch data only once per page load". if the user loads the page again, it will use the data in the cache and revalidate. Maybe the correct term is "revalidate only once". |
@oran1248 Depending on what you mean by "load the page again", I don't think Once again, apologies if I'm being misleading here and not really following your question. |
@nfantone You are right about page reloading, but what about router navigations? |
From what I understand, what you want is to fetch data the first time you need it and then never fetch it again? You can achieve that with the options @nfantone give you, this way SWR will not fetch the data if it is already in the cache, you can then use This data will be kept in the in-memory cache so page navigation using Next.js or React Router or any other Client-Side navigation will keep the data in cache avoiding further requests. I also think you have a misunderstanding in what revalidation is, revalidation means you already have the data and you are fetching it again to ensure is not stale, what you want is to avoid automatic revalidation to happen if you already have the data, the initial fetch is not a revalidation. Only exception of the last part is when you provide |
@sergiodxa loud and clear! thanks! |
Hello, I guess and I understand that it has to do with the options mentioned above by the user @nfantone:
So what should I do? Disable all of the above options. Or use natively without your library as in this example. const [question, setQuestion] = useState(defaultValues);
const [mounted, setMounted] = useState(false);
const [error, setError] = useState(false);
useEffect(() => {
async function initialData() {
const response = await fetch(config.apiNamespace + crud.apiRouter+'/'+props.id, {
method: 'GET'
})
let data = await response.json();
if (response.status !== 200){
setError(data)
}else{
setQuestion(data)
}
setMounted(true);
}
initialData().then()
},[])
if (!mounted) {
return (<Loader/>)
}
if (error) {
return (<ErrorPage statusCode={error.data.status} title={error.message} statusText={error.message}/>)
} Any clarification? |
@chiqui3d A couple of pointers here. First, if this is initial data that you are fetching, you might wanna give function MyComponent({ initialData }) {
const { data } = useSWR('/api/data', fetcher, { initialData });
// ...
} If you are using an SSR solution, like next.js, you could return Another thing I can think of is that it seems like you are binding the values of the object you fetch with {initialData && (
<form>
<input name="name" type="text" defaultValue={initialData.name} />
<input name="age" type="number" defaultValue={initialData.age} />
</form>
} |
Hello @nfantone, Thank you very much now using defaultValue instead of value, it works perfectly. Sorry for my rookie question, so is this what you recommend to use? Defaultvalue instead of value. This is my complete component using useSWR without Next.js in this case, but I usually use Next.js. import React, {useEffect, useState} from "react";
import {Breadcrumb, Breadcrumbs} from '@src/Layout/Breadcrumbs'
import FormBootstrap from "react-bootstrap/Form"
import TopActions from "@src/Form/TopActions";
import ResponseSuccessForm from "@src/Form/ResponseSuccessForm";
import ResponseFailForm from "@src/Form/ResponseFailForm";
import BottomActions from "@src/Form/BottomActions";
import {FetchWithResponse} from "@src/helpers/fetchWithResponse";
import ColWithInput from "@src/Form/ColWithInput";
import Row from "@src/Form/Row";
import Answer from "@src/Pages/questions/partials/Answer";
import config from "@root/config";
import Loader from "@src/Layout/Loader";
import ErrorPage from "@src/Layout/ErrorPage";
import useSWR from "swr";
import fetchSWR from "@src/helpers/fetchSWR";
const defaultValues = {
title: '',
answers: [
{
title: '',
image: '',
order: '',
question_id: ''
},
{
title: '',
image: '',
order: '',
question_id: ''
},
{
title: '',
image: '',
order: '',
question_id: ''
}
],
}
const crud = {
name: 'question',
pageName: 'Editar pregunta',
listName: 'Preguntas',
listRouter: 'questions/index',
actionApiRouter: 'questions',
submitButton: 'Actualizar pregunta',
submitAction: 'PUT',
actionMessage: 'Pregunta actualizada correctamente'
}
const QuestionsEdit = (props) => {
const [validated, setValidated] = useState(false)
const [responseForm, setResponseForm] = useState('')
const [responseFailForm, setResponseFailForm] = useState(null)
const [mounted, setMounted] = useState(false);
const [question, setQuestion] = useState(defaultValues);
const {data, error} = useSWR(config.apiNamespace + crud.actionApiRouter+'/'+props.id, url => fetchSWR(url, {
method: 'GET',
}, false));
useEffect(() => {
async function initialData() {
setQuestion(data)
setMounted(true)
}
initialData().then()
},[data])
if (!mounted) {
return (<Loader/>)
}
if (mounted && error) {
const statusText = error.statusText || error.toString()
return (<ErrorPage statusCode={error.status} title={statusText} statusText={statusText}/>)
}
const onChangeHandler = (event, index) => {
const element = event.target;
setQuestion(prevState => {
return {...prevState, [element.name]: element.value};
});
}
const handleSubmit = async (event) => {
const form = event.currentTarget
if (form.checkValidity() === false) {
event.preventDefault()
event.stopPropagation()
}
const imageErrors = [];
const ImageError = ({index}) => <div className="d-block">Por favor agrega la imagen para la respuesta {index}</div>
question.answers.forEach((answer,index) => {
if (answer['image'] === ''){
imageErrors.push(<ImageError key={"error-"+index} index={index+1}/>);
}
})
if (imageErrors.length > 0 ){
event.preventDefault()
event.stopPropagation()
setValidated(false);
setResponseFailForm(imageErrors);
return;
}
setValidated(true);
if (form.checkValidity() === true) {
event.preventDefault()
setResponseFailForm('')
setResponseForm('')
const successCallback = async (res, newData) => {
setValidated(false)
setResponseForm(crud.actionMessage);
};
const errorCallback = async (res, data) => {
setValidated(false)
setResponseFailForm('Error ' + data.status + '. ' + data.message)
};
return await FetchWithResponse(
config.apiNamespace + crud.actionApiRouter+'/'+question.id,
props,
JSON.stringify(question),
crud.submitAction,
true,
successCallback,
errorCallback
);
}
}
return (
<>
<Breadcrumbs currentIconClass="far fa-bookmark" currentPageName={crud.pageName}>
<Breadcrumb pageName={crud.listName} href={crud.listRouter}/>
<Breadcrumb current={true} pageName={crud.pageName}/>
</Breadcrumbs>
{question && mounted && <div className="row">
<div className="col-sm-12 mb-3">
<FormBootstrap autoComplete="off"
className={"form js-form-new-" + crud.name}
method="POST"
noValidate
validated={validated}
onSubmit={handleSubmit}
encType="multipart/form-data">
<TopActions backButton={{href: crud.listRouter}}
submitButton={{value: crud.submitButton}}/>
{responseForm && <ResponseSuccessForm successMessage={responseForm}/>}
{responseFailForm && <ResponseFailForm errorMessage={responseFailForm}/>}
<Row text="Titulo de la pregunta">
<ColWithInput
col="col-md-8"
name="title"
label={false}
placeholder="Introduce un titulo"
defaultValue={question.title}
onChange={onChangeHandler}
required={true}
/>
</Row>
<Answer data={question} setData={setQuestion}/>
<BottomActions backButton={{href: crud.listRouter}}
submitButton={{value: crud.submitButton}}/>
</FormBootstrap>
</div>
</div> }
</>
)
}
export default QuestionsEdit; Thanks again for your time. |
It has been complicated to me with the preview of the image, it is easier to leave it as it was before, that is to say that it is executed only once, that to have to be controlling the preview of the image and to come back to get the images through the DOM when sending the form. Seems to me more complicated. |
@chiqui3d Would you happen to have a runnable example you could share? It's kind of hard to follow what your issues are without something to collate against. |
Similar problem i have: https://stackoverflow.com/questions/66699292/where-to-calculate-groups-for-fluentui-detaillist-in-nextjs In fluentUI i want to calculate another state "groups" for a DetailedList based on returned data from swr, but then when user changes some state (similar to the input example) it then refetches with swr and then it recalculate the groups and i end up with revering the user action of collapsing UI. New at all this - feeling I miss the bigger picture. |
For whoever comes to this issue: Since 1.0.0, there’s a new |
I'm new to using useSWR also but I'm actually having a similar use case as the author. I'm using Next.js and getStaticProps with incremental static regeneration. When using ISR without SWR two refreshes are needed in order to see updates from the server (which is f*n annoying). If I add SWR only one refresh is needed (expected behaviour for the user). A lot of my page content doesn't need to be revalidated on focus etc (just unnecessary requests for the users). This is why I'm considering using SWR in a similar way as the author ie with most flags disabled. |
This may help. SWR has excellent deduplication capacity built-in: https://swr.vercel.app/docs/advanced/performance I was awaiting a lengthy In my case, the above immutable config options plus What did work was setting the
This way, SWR can complete its revalidation comparisons, which start when the component mounts and continue whilst the data's being manhandled, but the same API request isn't arbitrarily duplicated for at least an hour, even if the component remounts. |
i have an issue because my swr always fetching every time the animation moves, for example sidebar animation. <SWRConfig |
For late visitors just arriving... SWR now has "immutable mode": https://swr.vercel.app/blog/swr-v1.en-US#immutable-mode |
How can I use
swr
to fetch data only once? I don't need to refresh the data and revalidate.I prefer to use
swr
library for this and not just sending simple request and showing data, for couple of reasons:swr
manages cacheswr
prevent a situation of update after unmount (no need to cancel request)and more great things
swr
has to offer..So how can I use
swr
for this? What I need to put in theoptions
param?Is it a good idea to use it for this purpose? It feels like I'm using a sledgehammer to crack a nut.
Thanks!
The text was updated successfully, but these errors were encountered: