Hey there,
I am building an app using the React Native SDK. I am currently working on a preview screen where users can preview the camera, and test their mic and speaker, but I keep getting errors.
For the camera, when the screen first loads the camera starts and the video shows, but if I toggle it off and on again, I get ZoomVideoSDKError_Load_Module_Error.
For the Microphone test, I can start it and stop it without issue, but when I try to play it back using the testAudioDeviceHelper.playMicTest() function I get ZoomVideoSDKError_Wrong_Usage.
For the speaker test just get ZoomVideoSDKError_Internal_Error and nothing happens.
I am using "@zoom/react-native-videosdk": "^2.2.5" and Expo. I should note that the actual tour screen and functionality work great, without any issues; it is just the preview screen.
Here is the full code:
import React, { useState, useEffect } from 'react';
import { View, Text, Pressable } from 'react-native';
import { ZoomView, useZoom, ZoomVideoSDKTestMicStatus, EventType, VideoAspect, Errors } from '@zoom/react-native-videosdk';
import { Button } from '@/src/components/common/button';
import { ArrowLeft, Mic, MicOff, Video, VideoOff, Volume2, VolumeX, RotateCcw, Play } from 'lucide-react-native';
import { router } from 'expo-router';
interface PreviewScreenProps {
sessionName: string;
displayName: string;
onJoin: (videoOn: boolean, audioOn: boolean) => void;
initialVideoOn?: boolean;
initialAudioOn?: boolean;
}
export function PreviewScreen({
sessionName,
displayName,
onJoin,
initialVideoOn = false,
initialAudioOn = false
}: PreviewScreenProps) {
const zoom = useZoom();
const [videoOn, setVideoOn] = useState(initialVideoOn);
const [audioOn, setAudioOn] = useState(initialAudioOn);
const [micStatus, setMicStatus] = useState<ZoomVideoSDKTestMicStatus>(ZoomVideoSDKTestMicStatus.CanTest);
const [isSpeakerTesting, setIsSpeakerTesting] = useState(false);
useEffect(() => {
const testMicListener = zoom.addListener(
EventType.onTestMicStatusChanged,
(params: { status: ZoomVideoSDKTestMicStatus }) => {
console.log('Mic status changed:', params.status);
setMicStatus(params.status);
}
);
return () => {
testMicListener.remove();
}
}, [zoom]);
const handleMicAction = async () => {
try {
if (micStatus === ZoomVideoSDKTestMicStatus.CanTest) {
const res = await zoom.testAudioDeviceHelper.startMicTest();
console.log('Start Mic Test', res);
if (res === Errors.Success) {
setMicStatus(ZoomVideoSDKTestMicStatus.Recording);
}
} else if (micStatus === ZoomVideoSDKTestMicStatus.Recording) {
const res = await zoom.testAudioDeviceHelper.stopMicTest();
console.log('Stop Mic Test', res);
if (res === Errors.Success) {
setMicStatus(ZoomVideoSDKTestMicStatus.CanPlay);
}
} else if (micStatus === ZoomVideoSDKTestMicStatus.CanPlay) {
const res = await zoom.testAudioDeviceHelper.playMicTest();
console.log('Play Mic Test', res);
if (res === Errors.Success) {
setMicStatus(ZoomVideoSDKTestMicStatus.CanTest);
}
}
} catch (e) {
console.error("Mic test error", e);
}
};
const resetMicTest = async () => {
try {
if (micStatus !== ZoomVideoSDKTestMicStatus.CanPlay) return;
const res = await zoom.testAudioDeviceHelper.startMicTest();
console.log('resetMicTest', res);
if (res === Errors.Success) {
setMicStatus(ZoomVideoSDKTestMicStatus.Recording);
}
} catch (e) {
console.error('resetMicTest error', e);
}
};
const toggleSpeakerTest = async () => {
try {
if (isSpeakerTesting) {
const res = await zoom.testAudioDeviceHelper.stopSpeakerTest();
console.log('Stop Speaker Test', res);
if (res === Errors.Success) {
setIsSpeakerTesting(false);
}
} else {
const res = await zoom.testAudioDeviceHelper.startSpeakerTest();
console.log('Start Speaker Test', res);
if (res === Errors.Success) {
setIsSpeakerTesting(true);
}
}
} catch (e) {
console.error("Speaker test error", e);
}
};
const toggleVideo = async () => {
try {
if (videoOn) {
const res = await zoom.videoHelper.stopVideo();
console.log('Stop Video', res);
} else {
const res = await zoom.videoHelper.startVideo();
console.log('Start Video', res);
}
} catch (e) {}
setVideoOn(!videoOn);
}
useEffect(() => {
return () => {
zoom.videoHelper.stopVideo().catch(() => {});
}
}, []);
useEffect(() => {
return () => {
if (isSpeakerTesting) zoom.testAudioDeviceHelper.stopSpeakerTest().catch(() => {});
};
}, [isSpeakerTesting, zoom]);
return (
<View className="flex-1 relative px-6 py-4 gap-2 bg-[#FAF3EA]">
{/* Close Button */}
<View className="flex-row items-center gap-4 ">
<Pressable onPress={() => router.back()}>
<ArrowLeft color="#1D1C1B" size={28}/>
</Pressable>
<Text className="text-3xl font-medium text-black">Preview</Text>
</View>
<View className="flex-1 flex-row gap-2">
{/* Video Preview */}
<View className="flex-1 bg-black relative rounded-2xl overflow-hidden">
{videoOn ? (
<ZoomView
style={{ width: '100%', height: '100%' }}
userId=""
preview={true}
fullScreen={false}
sharing={false}
hasMultiCamera={false}
multiCameraIndex={'0'}
videoAspect={VideoAspect.PanAndScan}
/>
) : (
<View className="flex-1 items-center justify-center bg-[#232323]">
<View className="w-24 h-24 rounded-full bg-gray-600 items-center justify-center">
<Text className="text-white text-2xl font-bold">{displayName.charAt(0)}</Text>
</View>
<Text className="text-white mt-4 text-lg">Camera is off</Text>
</View>
)}
{/* Controls Overlay */}
<View className="absolute bottom-6 left-0 right-0 flex-row justify-center gap-6">
<View className="flex-row gap-4">
<Button variant="overlayIcon" onPress={() => setAudioOn(!audioOn)}>
{audioOn ? (
<Mic size={28} color="#FFF" />
) : (
<MicOff size={28} color="#FFF" />
)}
</Button>
<Button variant="overlayIcon" onPress={toggleVideo}>
{videoOn ? (
<Video size={28} color="#FFF" />
) : (
<VideoOff size={28} color="#FFF" />
)}
</Button>
</View>
</View>
</View>
<View className="justify-between bg-white rounded-2xl p-6">
<View>
<Text className="text-xl font-bold text-black mb-6">{sessionName}</Text>
<View className="gap-4">
{/* Mic Test Control */}
<View className="gap-2">
<Text className="text-sm font-medium text-gray-500 uppercase">Microphone</Text>
<View className="flex-row gap-2">
<Pressable
onPress={handleMicAction}
className={`flex-1 py-2.5 rounded-lg flex-row items-center justify-center gap-2 ${
micStatus === ZoomVideoSDKTestMicStatus.Recording
? 'bg-red-50 border border-red-100'
: micStatus === ZoomVideoSDKTestMicStatus.CanPlay
? 'bg-green-50 border border-green-100'
: 'bg-gray-50 border border-gray-200'
}`}
>
{micStatus === ZoomVideoSDKTestMicStatus.Recording ? (
<>
<View className="w-2 h-2 bg-red-500 rounded-sm" />
<Text className="text-red-600 font-medium text-sm">Stop</Text>
</>
) : micStatus === ZoomVideoSDKTestMicStatus.CanPlay ? (
<>
<Play size={14} color="#10B981" fill="#10B981" />
<Text className="text-green-600 font-medium text-sm">Play</Text>
</>
) : (
<Text className="text-gray-700 font-medium text-sm">Test Mic</Text>
)}
</Pressable>
{micStatus === ZoomVideoSDKTestMicStatus.CanPlay && (
<Pressable
onPress={resetMicTest}
className="w-10 items-center justify-center rounded-lg bg-gray-100 border border-gray-200"
>
<RotateCcw size={16} color="#6B7280" />
</Pressable>
)}
</View>
</View>
{/* Speaker Test Control */}
<View className="gap-2 mt-2">
<Text className="text-sm font-medium text-gray-500 uppercase">Speaker</Text>
<Pressable
onPress={toggleSpeakerTest}
className={`py-2.5 rounded-lg flex-row items-center justify-center gap-2 ${
isSpeakerTesting
? 'bg-blue-50 border border-blue-100'
: 'bg-gray-50 border border-gray-200'
}`}
>
{isSpeakerTesting ? (
<>
<VolumeX size={16} color="#2563EB" />
<Text className="text-blue-600 font-medium text-sm">Stop Sound</Text>
</>
) : (
<>
<Volume2 size={16} color="#374151" />
<Text className="text-gray-700 font-medium text-sm">Test Speaker</Text>
</>
)}
</Pressable>
</View>
</View>
</View>
<Button
title="Join Session"
onPress={() => onJoin(videoOn, audioOn)}
/>
</View>
</View>
</View>
);
}
Note that the entire component above is wrapped in the ZoomVideoSdkProvider with the following configs: { domain: "zoom.us", enableLog: true }
And here are the full logs from a test:
Start Mic Test ZoomVideoSDKError_Success
Stop Mic Test ZoomVideoSDKError_Success
Play Mic Test ZoomVideoSDKError_Wrong_Usage
resetMicTest ZoomVideoSDKError_Success
Stop Mic Test ZoomVideoSDKError_Success
Start Speaker Test ZoomVideoSDKError_Internal_Error
Start Speaker Test ZoomVideoSDKError_Internal_Error
Stop Video ZoomVideoSDKError_Load_Module_Error
Start Video ZoomVideoSDKError_Load_Module_Error
Any help or guidance is much appreciated! Thanks.