swr icon indicating copy to clipboard operation
swr copied to clipboard

Successful fetch does not cause rerender on React Native

Open mmasiulis opened this issue 2 years ago • 12 comments

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.

mmasiulis avatar Feb 01 '23 08:02 mmasiulis

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

tomasz89nowak avatar Feb 06 '23 16:02 tomasz89nowak

Same problem here in my react-native project

eduduardo avatar Feb 09 '23 00:02 eduduardo

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]
  });

eduduardo avatar Feb 09 '23 12:02 eduduardo

same problem, use 1.2.0 version, can work

ybf970928 avatar Feb 14 '23 05:02 ybf970928

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.

MonstraG avatar Feb 16 '23 10:02 MonstraG

I can confirm this. I'm also using the custom setup the docs suggest to use for React Native.

EzeRangel avatar Mar 13 '23 19:03 EzeRangel

The 2.1.0 version I am currently using still has this problem in RN.

image

Bruce-zxy avatar Mar 19 '23 17:03 Bruce-zxy

My case was fixed by updating to SWR 2.1.4 (PR that fixed it - https://github.com/vercel/swr/pull/2576).

MonstraG avatar Apr 25 '23 10:04 MonstraG

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.

cdiddy77 avatar May 18 '23 13:05 cdiddy77

@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

MonstraG avatar Sep 25 '23 16:09 MonstraG

same here , any help please?

KingAmo avatar Oct 08 '23 09:10 KingAmo