swr
swr copied to clipboard
Successful fetch does not cause rerender on React Native
Bug report
Description / Observed Behavior
On React Native project I've decided to try out latest(2.0.3) swr version (I previously was using 1.3.0 version). I've encountered issue that upon successful retrieval of data, the useSwr
data
property does not change - is undefined, isLoading
also stays true
, but for some reason onSuccess
is called. Data and isLoading changes only whenever I change screen - this causes rerender and data
property gets filled with data, isLoading is set to false.
Expected Behavior
I expect the useSwr
hook to change its property data
to received value whenever call is successful, just like in 1.3.0
version.
Repro Steps / Code Example
const fetchCampaignById = async (id: string) => {
return await api.fetchCampaignDetails(id, language as LanguageCode)
}
const { data, isLoading } = useSwr(
['campaigns', donations],
() => fetchCampaignById(donations[0].campaignId),
{
onSuccess: () => {
console.log('success callback')
},
}
)
where api.fetchCampaign
is a Promise
which returns directly array of data once promise is resolved. Success callback is logged, api calls also do not fail when inspecting network debugger, yet data property does not change
Additional Context
SWR version - 2.0.3
Add any other context about the problem here.
I also run on this issue.
The request from the server is resolved, but data
is still undefined, isLoading
still true, isValidating
is also true. When I blur and refocus the browser, the data
is ready and those values turn into false.
Here is an example that fails:
function sleep() {
return new Promise<void>((resolve) => {
setTimeout(() => {
resolve();
}, 1);
});
}
const fetcher = async (arg: string) => {
await sleep();
return [];
};
const response = useSWR("test", fetcher);
When I change that 1 millisecond to 1000, it works well.
App is built with next.js 13.0.2 and SWR 2.0.3, reactStrictMode: true
Same problem here in my react-native project
Found a workaround solution, creating a middleware and forcing the re-render:
import useSWR, { SWRHook, Middleware } from 'swr';
const forceUpdate: Middleware =
(useSWRNext: SWRHook) => (key, originalFetcher, config) => {
const [, setState] = useState({});
const updatedConfig = {
...config,
onSuccess: (data: any) => {
setTimeout(() => setState({}), 0);
if (config && config.onSuccess) {
config.onSuccess(data, key, config);
}
},
onError: (err: any) => {
setTimeout(() => setState({}), 0);
if (config && config.onError) {
config.onError(err, key, config);
}
},
};
const swr = useSWRNext(key, originalFetcher, updatedConfig);
return swr;
};
const { data, isLoading } = useSWR(`https://pokeapi.co/api/v2/pokemon`, fetcher, {
use: [forceUpdate]
});
same problem, use 1.2.0 version, can work
Managed to reproduce this in nextjs:
"dependencies": {
"next": "^13.1.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"swr": "^2.0.3"
},
https://github.com/MonstraG/swr-race-condition-bug
For me it happens approximately half of the time when refreshing the page. trip time to endpoints is about 60 ms in console.
Because it happens sometimes and not always - seems like a race condition thing.
React strict mode must be off to repro it!
(also happens in prod in a real app)
Workaround: add
const [_, setState] = useState<number>(0);
useSWR(...) {
...
onSuccess: () => setTimeout(() => setState(new Date().valueOf()), 1)
...
}
to trigger the rerender manually.
I can confirm this. I'm also using the custom setup the docs suggest to use for React Native.
The 2.1.0 version I am currently using still has this problem in RN.
My case was fixed by updating to SWR 2.1.4 (PR that fixed it - https://github.com/vercel/swr/pull/2576).
I found that in my app, even using a timer (no I/O) the successful fetch failed to cause a re-render. But then when I tried to reproduce it in a simple standalone app, it worked correctly 🤷
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
async function fetcher(key: string) {
console.log('fetcher-pre');
await sleep(4000);
console.log('fetcher-post');
return {message: `Hello ${key}!`};
}
function App(): JSX.Element {
const {data, error, isLoading} = useSWR('It got loaded', fetcher);
return (
<SafeAreaView style={backgroundStyle}>
<View>
<Text>{data?.message || 'loading'}</Text>
</View>
</ScrollView>
</SafeAreaView>
);
}
We have reverted to 1.2 for now.
@huozhi, you probably want to reopen https://github.com/vercel/swr/issues/2446 too, as it was also fixed by the same pr that now was reverted
same here , any help please?