1

I've been battling a bug in my code for the last 4 days and would appreciate some pointers to get me going in the right directions. Component is working fine as long as there is internet connection, but if there is no internet connection, audios and videos are not playing, only thumbnail present.

I'm using netInfo's NetInfo.fetch() to check for connection. If there is connection, I'm refetching data to check for any updates to student assignments.

I'm using expo-av for playing audio/video files (v10.2.1). I'm also using useQuery hook from react-query to fetch data about audio and video files (like url etc.) My video player component is something like this:

Video Player:

import React, {
  forwardRef,
  ForwardRefRenderFunction,
  useCallback,
  useImperativeHandle,
  useRef
} from 'react';
import { Platform } from 'react-native';
import Orientation from 'react-native-orientation-locker';
import { Audio, Video, VideoFullscreenUpdateEvent, VideoProps } from 'expo-av';

const Player: ForwardRefRenderFunction<
  Video | undefined,
  VideoProps
> = (props, ref) => {
  const innerRef = useRef<Video>(null);

  const orientation = useCallback<
    (event: VideoFullscreenUpdateEvent) => void
  >(
    (event) => {
      if (Platform.OS === 'android') {
        if (
          event.fullscreenUpdate === Video.FULLSCREEN_UPDATE_PLAYER_DID_PRESENT
        ) {
          Orientation.unlockAllOrientations();
        } else if (
          event.fullscreenUpdate === Video.FULLSCREEN_UPDATE_PLAYER_DID_DISMISS
        ) {
          Orientation.lockToPortrait();
        }
      }

      props.onFullscreenUpdate?.(event);
    },
    [props]
  );

  useImperativeHandle(ref, () => {
    if (innerRef.current) {
      return innerRef.current;
    }

    return undefined;
  });

  return (
    <Video
      resizeMode="contain"
      useNativeControls
      ref={innerRef}
      onLoad={loading}
      {...props}
      onFullscreenUpdate={orientation}
    />
  );
};

export const VideoPlayer = forwardRef(Player);

Custom Hook:

For async state management, I'm using a custom react-query hook, that looks something like this (non-relevant imports and code removed):

import { useFocusEffect } from '@react-navigation/core';
import { useCallback } from 'react';
import NetInfo from '@react-native-community/netinfo';

export const useStudentAssignment = (
  assignmentId: Assignment['id']
): UseQueryResult<Assignment, Error> => {
  
  const listKey = studentAssignmentKeys.list({ assignedToIdEq: studentData?.id });
  const queryClient = useQueryClient();

  const data = useQuery<Assignment, Error>(
    studentAssignmentKeys.detail(assignmentId),
    async () => {
      const { data: assignment } = await SystemAPI.fetchAssignment(assignmentId);

      return Assignment.deserialize({
        ...assignment,
      });
    },
    {
      staleTime: 1000 * 60 * 30,
      initialData: () => {
        const cache= queryClient.getQueryData<Assignment[]>(listKey);

        return cache?.find((assignment) => assignment.id === assignmentId);
      },
      initialDataUpdatedAt: queryClient.getQueryState(listKey)?.dataUpdatedAt,
    }
  );

  useFocusEffect(
    useCallback(() => {
      NetInfo.fetch().then((state) => {
        if (state.isConnected) {
          data.refetch();
        }
      });      
    }, [data])
  );

  return data;
};

Component:

import React, { FC, useCallback, useEffect, useMemo, useRef } from 'react';
import { SafeAreaView } from 'react-native-safe-area-context';
import { StackScreenProps } from '@react-navigation/stack';
import { ROUTES } from 'enums/SMSRoutes';
import { StoreType } from 'enums/SMSStoreType';
import { useStudentAssignment } from 'hooks/Assignments/useStudentAssignment';
import { RootStackParamList } from 'navigators';
import { AssignmentViewer } from 'screens/AssignmentViewer';
    
type NavProps = StackScreenProps<
  RootStackParamList,
  ROUTES.ASSIGNMENT_VIEW
>;

export const AssignmentView: FC<NavProps> = ({
  navigation,
  route: {
    params: { assignmentId }
  }
}) => {
  const assignmentQuery = useStudentAssignment(assignmentId);
  const assignmentTracker = useStore(StoreType.AssignmentTracker);
  const isDoneRef = useRef<boolean>(false);

  const questions = assignmentQuery.data?.questions || [];

  const activeQuestion = useMemo(() => {
    return questions.filter((question) => question.active);
  }, [questions]);

  const onDone = useCallback(() => {
    isDoneRef.current = true;
    navigation.push(ROUTES.ASSIGNMENT_COMPLETED);
  }, [navigation]);

  useEffect(() => {
    assignmentTracker.start(assignmentId);

    return () => {
        assignmentTracker.finish(isDoneRef.current);
    };
  }, []);

  return (
    <SafeAreaView>
      <AssignmentViewer
        questions={activeQuestion}
        onDone={onDone}
        isLoading={assignmentQuery.isLoading}
      />
    </SafeAreaView>
  );
};

What I'm trying to do here is that if internet connection is connected and the user navigates to the current view (which is used to view assignments), I'd like to refetch the data. Per the requirements, I can't use the staleTime property or any other interval based refetching.

Component is working fine if I don't refetch, or if internet connection is present. If connection isn't there, it doesn't play the cache'd audio/video.

If I take out the check for internet connection (remove netInfo), component display videos both offline and online. However, refetching fails due to no connectivity.

What should I change to make sure that data is refetched when connected and videos are played even if not connected to Internet?

Ken White
  • 123,280
  • 14
  • 225
  • 444
SJaka
  • 712
  • 13
  • 40

0 Answers0