Skip to content

Instantly share code, notes, and snippets.

@te-online
Last active November 25, 2020 15:33
Show Gist options
  • Save te-online/4eaa564aa02546ea25247d2f079f935e to your computer and use it in GitHub Desktop.
Save te-online/4eaa564aa02546ea25247d2f079f935e to your computer and use it in GitHub Desktop.
Forces SWR to revalidate in react-native environment
/**
* Author: @nandorojo, adapted from https://github.com/vercel/swr/issues/417#issuecomment-721438386
* Forces SWR to revalidate when one of the following events occurs
* - React-Navigation event (focus)
* - Focus of the app through AppState is propagated
* - Networks access is restored
*/
import { responseInterface, ConfigInterface } from 'swr';
import { useRef, useEffect } from 'react';
import { AppState } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import NetInfo, { NetInfoState } from '@react-native-community/netinfo';
type Props<Data, Error> = {
/**
* Required: pass the `revalidate` function returned to you by SWR.
*/
revalidate: responseInterface<Data, Error>['revalidate'];
} & Pick<
ConfigInterface,
'revalidateOnFocus' | 'revalidateOnReconnect' | 'focusThrottleInterval'
>;
import { Platform } from 'react-native';
/**
* swr-react-native
*
* This helps you revalidate your SWR calls, based on navigation actions in `react-navigation`.
*/
export default function useSWRReactNavigation<Data = any, Error = any>(
props: Props<Data, Error>,
) {
const {
revalidate,
// copy defaults from SWR
revalidateOnFocus = true,
revalidateOnReconnect = true,
focusThrottleInterval = 5000,
} = props;
const { addListener } = useNavigation();
const lastFocusedAt = useRef<number | null>(null);
const focusCount = useRef<number>(0);
const fetchRef = useRef(revalidate);
useEffect(() => {
fetchRef.current = revalidate;
});
const previousAppState = useRef(AppState.currentState);
const previousNetworkState = useRef<NetInfoState | null>(null);
useEffect(() => {
// SWR does all of this on web.
if (Platform.OS === 'web') return;
let unsubscribeReconnect: ReturnType<
typeof NetInfo.addEventListener
> | null = null;
if (revalidateOnReconnect) {
unsubscribeReconnect = NetInfo.addEventListener((state) => {
if (
previousNetworkState.current?.isInternetReachable === false &&
state.isConnected &&
state.isInternetReachable
) {
fetchRef.current();
}
previousNetworkState.current = state;
});
}
const onFocus = () => {
// First focus is replaced by SWR's first load event
if (focusCount.current < 1) {
focusCount.current = 1;
return;
}
const isThrottled =
focusThrottleInterval &&
lastFocusedAt.current &&
Date.now() - lastFocusedAt.current <= focusThrottleInterval;
if (!isThrottled) {
lastFocusedAt.current = Date.now();
fetchRef.current();
}
};
const onAppStateChange = (nextAppState: AppState['currentState']) => {
if (
previousAppState.current.match(/inactive|background/) &&
nextAppState === 'active'
) {
onFocus();
}
previousAppState.current = nextAppState;
};
let unsubscribeFocus: ReturnType<typeof addListener> | null = null;
if (revalidateOnFocus) {
unsubscribeFocus = addListener('focus', onFocus);
AppState.addEventListener('change', onAppStateChange);
}
return () => {
if (revalidateOnFocus) {
unsubscribeFocus?.();
AppState.removeEventListener('change', onAppStateChange);
}
if (revalidateOnReconnect) {
unsubscribeReconnect?.();
}
};
}, [
addListener,
focusThrottleInterval,
revalidateOnFocus,
revalidateOnReconnect,
]);
}
@nandorojo
Copy link

nandorojo commented Nov 25, 2020

I believe line 76 should read === 0, right? Or <1

@te-online
Copy link
Author

Yes! Thanks 😊 Hope it's okay I copied your code into a gist?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment