Skip to content

Instantly share code, notes, and snippets.

@vincicat
Last active March 10, 2023 21:35
Show Gist options
  • Save vincicat/261c79e3ccf75f8f272e0493f250303f to your computer and use it in GitHub Desktop.
Save vincicat/261c79e3ccf75f8f272e0493f250303f to your computer and use it in GitHub Desktop.
Single-File JavaScript Example: react-query in React Native (0.70)
/* eslint-disable react-native/no-inline-styles */
import * as React from 'react';
import movies from './data/movies.json';
import {
AppStateStatus,
Platform,
AppState,
TouchableHighlight,
ActivityIndicator,
View,
Text,
FlatList,
RefreshControl,
} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {
QueryClient,
QueryClientProvider,
focusManager,
} from '@tanstack/react-query';
import {useFocusEffect} from '@react-navigation/native';
import {useEffect} from 'react';
import {createNativeStackNavigator as createStackNavigator} from '@react-navigation/native-stack';
import NetInfo from '@react-native-community/netinfo';
import {onlineManager, useQuery} from '@tanstack/react-query';
// hook
export function useAppState(onChange) {
useEffect(() => {
AppState.addEventListener('change', onChange);
return () => {
AppState.removeEventListener('change', onChange);
};
}, [onChange]);
}
export function useOnlineManager() {
React.useEffect(() => {
// React Query already supports on reconnect auto refetch in web browser
if (Platform.OS !== 'web') {
return NetInfo.addEventListener(state => {
onlineManager.setOnline(
state.isConnected != null &&
state.isConnected &&
state.isInternetReachable,
);
});
}
}, []);
}
export function useRefreshByUser(refetch) {
const [isRefetchingByUser, setIsRefetchingByUser] = React.useState(false);
async function refetchByUser() {
setIsRefetchingByUser(true);
try {
await refetch();
} finally {
setIsRefetchingByUser(false);
}
}
return {
isRefetchingByUser,
refetchByUser,
};
}
export function useRefreshOnFocus(refetch) {
const enabledRef = React.useRef(false);
useFocusEffect(
React.useCallback(() => {
if (enabledRef.current) {
refetch();
} else {
enabledRef.current = true;
}
}, [refetch]),
);
}
// api
function delay(t) {
return new Promise(function (resolve) {
setTimeout(resolve, t);
});
}
async function fetchMovies() {
console.log('fetchMovies');
await delay(200 + Math.floor(Math.random() * 2000));
return movies
.slice(0, 100)
.map(movie => ({title: movie.title, year: movie.year}));
}
async function fetchMovie(title) {
console.log('fetchMovie', title);
await delay(200 + Math.floor(Math.random() * 2000));
const result = movies.filter(item => item.title === title);
if (result.length === 0) {
throw new Error('Movie not found');
}
return result[0]; //promise
}
// router
const Stack = createStackNavigator();
function MoviesListScreen({navigation}) {
const {isLoading, error, data, refetch} = useQuery(['movies'], fetchMovies);
const {isRefetchingByUser, refetchByUser} = useRefreshByUser(refetch);
useRefreshOnFocus(refetch);
const onListItemPress = React.useCallback(
movie => {
navigation.navigate('MovieDetails', {
movie,
});
},
[navigation],
);
const renderItem = React.useCallback(
({item}) => {
return (
<>
<TouchableHighlight onPress={onListItemPress}>
<View>
<Text>{JSON.stringify(item)}</Text>
</View>
</TouchableHighlight>
</>
);
},
[onListItemPress],
);
if (isLoading) {
return <ActivityIndicator />;
}
if (error) {
return <Text>{error.message}</Text>;
}
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={item => item.title}
ItemSeparatorComponent={() => (
<View style={{borderBottomWidth: 1, borderBottomColor: '#bbb'}} />
)}
refreshControl={
<RefreshControl
refreshing={isRefetchingByUser}
onRefresh={refetchByUser}
/>
}
/>
);
}
function MovieDetailsScreen(props) {
return (
<View>
<Text>{JSON.stringify(props)}</Text>
</View>
);
}
function MoviesStack() {
return (
<Stack.Navigator initialRouteName="MoviesList">
<Stack.Screen
name="MoviesList"
component={MoviesListScreen}
options={{
headerTitle: 'Movies',
}}
/>
<Stack.Screen
name="MovieDetails"
component={MovieDetailsScreen}
options={{
headerTitle: 'Movie details',
}}
/>
</Stack.Navigator>
);
}
function onAppStateChange(status) {
// React Query already supports in web browser refetch on window focus by default
if (Platform.OS !== 'web') {
focusManager.setFocused(status === 'active');
}
}
const queryClient = new QueryClient({
defaultOptions: {queries: {retry: 2}},
});
export default function App() {
useOnlineManager();
useAppState(onAppStateChange);
return (
<QueryClientProvider client={queryClient}>
<NavigationContainer>
<MoviesStack />
</NavigationContainer>
</QueryClientProvider>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment