# HandleRender helper, useQuery w/ internet check - Thurs 22 Oct, 020 Productive day yesterday and learnt a lot. ### `HandleRender` component Built a component to manage the different render states of most of the C+ screens. For example: nearly all should have a no internet state, no results state, "has content" state: ```typescript= interface HandleRenderProps { /** Loading state from Apollo's useQuery/useMutation hooks */ loading: boolean /** Error state from Apollo's useQuery/useMutation hooks */ error: ApolloError | undefined /** If the content should be displayed, you can pass a custom evaluation here (e.g. if comments array length > 0) */ hasContent: boolean /** A way for the user to retry the network request (usually `refetch`) */ onPress: () => void /** The title to be shown when simply no content. Defaults to generic message. */ noContentTitle?: string /** Optional no content description */ noContentDescription?: string } export const HandleRender: React.FC<HandleRenderProps> = ({ children, loading, error, hasContent, onPress, noContentTitle = ERROR_MESSAGES.noContent.title, noContentDescription = ERROR_MESSAGES.noContent.description, }) => { const netInfo = useNetInfo() /** netInfo.isInternetReachable is checking if the internet is available which means there is a second where it returns null/undefined while its performing the check, which can cause a flash of the wrong content. Assume internet is connected unless netInfo.isInternetReachable explicitly returns false. */ const isInternetReachable = netInfo.isInternetReachable ?? true switch (true) { /** if loading */ case loading: return <Loading /> /** if there is content */ case hasContent: return <>{children}</> /** if there is no internet */ case !isInternetReachable: return ( <Error errorType="no internet" title={ERROR_MESSAGES.noInternet.title} description={ERROR_MESSAGES.noInternet.description} onPress={async () => { try { await onPress() } catch (error) { // eslint-disable-next-line no-console console.warn('Error refetching: ', error) } }} /> ) /** if there is a problem fetching data */ case Boolean(error): return ( <Error errorType="error" title={ERROR_MESSAGES.general} onPress={async () => { try { await onPress() } catch (error) { // eslint-disable-next-line no-console console.warn('Error refetching: ', error) } }} /> ) /** if there isn't any content, but there is internet and no errors */ default: return <Error errorType="no results" title={noContentTitle} description={noContentDescription} /> } } ``` ### Custom `useQuery` / `useMutation` hooks I found the existing Apollo Client hooks don't give good errors when there is no internet connection (at least on React Native). As such, I extended the hooks to use the NetInfo information too: ```typescript= type MutationTuple<TData, TVariables> = [ (options?: MutationFunctionOptions<TData, TVariables>) => Promise<FetchResult<TData>>, MutationResult<TData> & { netInfo: NetInfoState } ] /** Extends Apollo's `useMutation` hook to include network status information */ export const useMutation = <TData = any, TVariables = OperationVariables>( mutation: DocumentNode, options?: MutationHookOptions<TData, TVariables> ): MutationTuple<TData, TVariables> => { const netInfo = useNetInfo() const base = useApolloMutation<TData, TVariables>(mutation, options) /** netInfo.isInternetReachable is checking if the internet is available which means there is a second where it returns null/undefined while its performing the check, which can cause a flash of the wrong content. Assume internet is connected unless netInfo.isInternetReachable explicitly returns false. */ const isInternetReachable = netInfo.isInternetReachable ?? true const loading = base[1].loading && isInternetReachable return [ base[0], { ...base[1], loading, netInfo: { ...netInfo, isInternetReachable, } as NetInfoState, }, ] } ``` I had to type assert `NetInfoState` due to some incorrect types with the library. ### MockedProvider errors and loading It's now much easier to test your component's `loading` and `error` states with Apollo Client 3's `MockedProvider`: ```typescript= export const myQuery: MockedResponse<MyQuery> = { request: { query: MY_QUERY, }, result: { data: { queryData: stuff, }, }, // will be loading for 2 seconds delay: 2000, // will then resolve to an error error: { name: 'Error', message: 'There was an error', }, } ``` ###### tags: `programmingjournal` `2020` `C+` `handlerender` `errorhandling` `useQuery` `useMutation` `mockedprovider`