Search code examples
typescriptreact-nativeexpoexpo-av

Expo-AV onPlaybackStatusUpdate type error: 'AVPlaybackStatus' to 'SetStateAction<{ isLooping: boolean; isPlaying: boolean; }>'


I'm getting started with react-native with expo and typescript. Currently trying to implement a Video-Playback component into my app.

I'm following the example in the official docs and a youtube video, which uses almost exactly the same example code.

I had to make some modifications to the code to satisfy typescript (see comments):

import { AVPlaybackStatus, ResizeMode, Video } from "expo-av";
import { useRef, useState } from "react";
import { Button, Text, View } from "react-native";
import { StyleSheet } from "react-native";

export default function Index() {
  const video = useRef<Video>(null); // without '<Video>' I'd get a lot of errors below in the jsx
  // const [status, setStatus] = useState({}); //this is how it is in both the docs & yt tutorial
  const [status, setStatus] = useState({
    isLooping: false,
    isPlaying: false,
    //...
  }); //I had to initialize these values otherwise I'd get errors below ('status.isPlaying' & 'status.isLooping')


  
  return (
    <View style={styles.container}>
      <Video
        ref={video}
        style={styles.video}
        source={{
          uri: 'https://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4',
        }}
        useNativeControls
        resizeMode={ResizeMode.CONTAIN}
        isLooping
        onPlaybackStatusUpdate={status => setStatus(() => status)} //exactly as in the docs, doesn't work
        // onPlaybackStatusUpdate={(status: AVPlaybackStatus) => setStatus(() => status)} //also doesn't work
        // onPlaybackStatusUpdate={setStatus} //also doesn't work
        // onPlaybackStatusUpdate={(status: AVPlaybackStatus) {
        //   setStatus(status);
        // }} //nothing works
      />

      {/* had to add some exclamation marks to satisfy typescript */}
      <View style={styles.buttons}>
        <Button title="Play from 5s" onPress={() => video.current!.playFromPositionAsync(5000)}/>
          <Button title={status.isLooping ? "Set to not loop": "Set to loop"} onPress={()=>video.current!.setIsLoopingAsync(!status.isLooping)}/>
        <Button
          title={status.isPlaying ? 'Pause' : 'Play'}
          onPress={() =>
            status.isPlaying ? video.current!.pauseAsync() : video.current!.playAsync()
          }
        />
      </View>

    </View>
  );
}


const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  video: {
    flex: 1,
    alignSelf: 'stretch'
  },
  buttons: {
    // color: 'blue',
    flex: 1,
    // alignSelf: 'stretch'
  }
});

status is underlined in red and I get the following error when hovering over it:

Argument of type '() => AVPlaybackStatus' is not assignable to parameter of type 'SetStateAction<{ isLooping: boolean; isPlaying: boolean; }>'.
  Type '() => AVPlaybackStatus' is not assignable to type '(prevState: { isLooping: boolean; isPlaying: boolean; }) => { isLooping: boolean; isPlaying: boolean; }'.
    Type 'AVPlaybackStatus' is not assignable to type '{ isLooping: boolean; isPlaying: boolean; }'.
      Type 'AVPlaybackStatusError' is missing the following properties from type '{ isLooping: boolean; isPlaying: boolean; }': isLooping, isPlayingts(2345)

Solution

  • I think you have to simply change

    const [status, setStatus] = useState({
        isLooping: false,
        isPlaying: false,
        //...
      })
    

    to

    const [status, setStatus] = useState<AVPlaybackStatus>()