Skip to content

Instantly share code, notes, and snippets.

@vincicat
Last active March 10, 2023 21:50
Show Gist options
  • Save vincicat/1ba1abbbdf4348c3183cf48b90ea0a36 to your computer and use it in GitHub Desktop.
Save vincicat/1ba1abbbdf4348c3183cf48b90ea0a36 to your computer and use it in GitHub Desktop.
Single-File JavaScript Example: useSWR in React Native
/* eslint-disable react-native/no-inline-styles */
import React, {useEffect, useRef} from 'react';
import {View, Text, AppState, StyleSheet, SafeAreaView} from 'react-native';
import NetInfo from '@react-native-community/netinfo';
import useSWR, {SWRConfig} from 'swr';
import {FlatList} from 'react-native-gesture-handler';
const randomInt = range => Math.floor(Math.random() * range);
// https://github.com/vercel/swr/discussions/532
const initFocusOld = revalidate => {
let appState = AppState.currentState;
const onAppStateChange = nextAppState => {
if (appState.match(/inactive|background/) && nextAppState === 'active') {
revalidate();
}
console.log('state change', nextAppState);
appState = nextAppState;
};
AppState.addEventListener('change', onAppStateChange);
return () => {
AppState.removeEventListener('change', onAppStateChange);
};
};
// offical. note: callback actually is revalidate
const initFocus = callback => {
let appState = AppState.currentState;
const onAppStateChange = nextAppState => {
/* If it's resuming from background or inactive mode to active one */
if (appState.match(/inactive|background/) && nextAppState === 'active') {
console.log('<App> refocusing. refreshing.');
callback();
}
appState = nextAppState;
};
// Subscribe to the app state change events
const subscription = AppState.addEventListener('change', onAppStateChange);
return () => {
subscription.remove();
};
};
//
// https://github.com/huozhi/swr-rn-example
function Page() {
const key1 = 'hello';
const key2 = 'initial';
const key3 = 'change by time';
const key4 = 'change on foreground';
const {data: data1} = useSWR(key1);
const {data: data2} = useSWR(key2);
const {data: data3, mutate: mutateKeyTimer} = useSWR(
key3,
() => `${randomInt(200)}`,
{
revalidateOnFocus: false,
},
);
const {data: data4} = useSWR(key4, () => `${randomInt(200)}`);
useEffect(() => {
const timer = setInterval(() => {
mutateKeyTimer();
}, 2000);
return () => clearInterval(timer);
}, [key3, mutateKeyTimer]);
const keyValuePairs = [
['KEY', 'VALUE'],
[key1, data1],
[key2, data2],
[key3, data3],
[key4, data4],
];
return (
<View style={styles.table}>
{keyValuePairs.map(([key, value]) => (
<View key={key} style={styles.row}>
<Text style={{fontWeight: 'bold'}}>{key}</Text>
<Text>{value}</Text>
</View>
))}
</View>
);
}
// from https://blog.logrocket.com/handling-data-fetching-next-js-useswr/
function UserPage() {
const address = 'https://randomuser.me/api/?results=6';
const fetcher = url => fetch(url).then(res => res.json());
const {data, error} = useSWR(address, fetcher);
let content;
if (error) {
content = <Text>Loading failed...</Text>;
}
if (!data) {
content = <Text>Loading...</Text>;
} else {
console.log('Received', data.length);
if (!data.results) {
content = <Text>Malformed data: {JSON.stringify(data)}.</Text>;
} else {
console.log('Received', Object.keys(data));
}
}
return (
<>
{data && data.results ? (
<FlatList
data={data.results}
keyExtractor={(item, index) => `user-list${index}`}
renderItem={({item}) => {
return (
<View
style={{
padding: 10,
borderBottomColor: '#bbb',
borderBottomWidth: 1,
}}>
<Text>
{item.name.first} {item.name.last}
</Text>
<Text>Country: {item.location.country}</Text>
<Text>State: {item.location.state}</Text>
<Text>Email: {item.email}</Text>
<Text>Phone: {item.phone}</Text>
<Text>Age: {item.dob.age}</Text>
</View>
);
}}
/>
) : (
content
)}
</>
);
}
function AppContainer() {
return (
<SafeAreaView>
<UserPage />
</SafeAreaView>
);
}
export default function App() {
// const prev = useRef(null);
return (
<SWRConfig
value={{
provider: () => new Map(),
isVisible: () => {
return true;
},
initFocus,
initReconnect: revalidate => {
let prev = {}; //closureeeeee
const unsubscribe = NetInfo.addEventListener(state => {
const ts = Date.now();
console.log('[network] prev: ', `--${ts}--`, JSON.stringify(prev));
// replace prev with prev.current if you refer ref version
if (
// possible value: null > true or false
prev.isInternetReachable === false &&
state.isConnected &&
state.isInternetReachable
) {
console.log('<App> reconnected. refreshing');
revalidate();
}
// prev.current = state;
prev = state;
console.log('[network] curr: ', `--${ts}--`, JSON.stringify(state));
console.log('[network] saved: ', `--${ts}--`, JSON.stringify(prev));
});
return () => {
//clean up
unsubscribe();
};
},
}}>
<AppContainer />
</SWRConfig>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
alignSelf: 'center',
flex: 1,
},
table: {
maxHeight: 120,
alignSelf: 'center',
alignItems: 'stretch',
},
row: {
flex: 1,
alignSelf: 'stretch',
justifyContent: 'space-between',
flexDirection: 'row',
width: 200,
},
cell: {
flex: 1,
alignSelf: 'stretch',
},
});
@vincicat
Copy link
Author

Note: this one is a huge pain for RN (see #417), official doc is unclear (not include an example is okay as RN App is huge, but they don't even give a repo🫠), example are not much (another notable mention), and the library may not be future-proof by some boilerplate author (example: React Native Template New Architecture)

Move to either Apollo (GraphQL API but RESTful can work too) or react-query (RESTful API) will be a better choice 🥲. [link]

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