'How to resolve this reference error : Audio is not defined

Problem

I'm trying to play some audio files in some specific situations. e.g)

  • When users access to login page, the audio plays 'Please enter your phone number'
  • when an error message alert comes up, audio file is played such as 'your phone number has been already registered'

So far, the audio files are played successfully when you access some pages, but I got the reference error in the image after I added two lines of code below in the root component (app.tsx)

error message screenshot

import {kioskAudio} from '../src/common/utils/kioskAudio';
const {playAudio, stopAudio} = kioskAudio();

What I've tried to resolve this issue

First try: I imported 'kioskAudio' method into KioskAlertError component directly. But I got the same reference error.

Second try: So I thought, 'Then should I import the 'kioskAudio' method to the root component(app.tsx) and deliver the props(playAudio, stopAudio) to the component like this :

                <KioskAlertError playAudio={playAudio} stopAudio={stopAudio} /> 

But I still got the reference error. How can I resolve this issue?

Source Code

app.tsx
import KioskAlert, {IKioskAlertProps} from './component/KioskAlert';
import KioskAlertError from './component/KioskAlertError';
import {kioskAudio} from '../src/common/utils/kioskAudio';


export default function CustomApp({Component, pageProps}) {
    const router = useRouter();
    const [shouldStartRender, setShouldStartRender] = useState(false);
    const [kioskAlertInfo, setKioskAlertInfo] = useState({isShow: false, onOK: null} as IKioskAlertProps);
    const {playAudio, stopAudio} = kioskAudio();

    useEffect(() => {
        setShouldStartRender(true);
    }, [router]);



    return (
        <>

            <KioskAlertContext.Provider
                value={{
                    kioskAlertState: kioskAlertInfo,
                    openKioskAlert: openKioskAlert,
                    closeKioskAlert: closeKioskAlert,
                }}
            >
                <SWRConfig
                    value={{
                        refreshInterval: 0,
                        revalidateOnReconnect: true,
                        revalidateOnFocus: true,
                        onErrorRetry: (error, key, config, revalidate, {retryCount}) => {
                            if (error.response?.status === 401) {
                                localStorage.removeItem('accessToken');
                                return;
                            }

                            if (retryCount >= 5) return;

                            setTimeout(() => {
                                revalidate({retryCount});
                            }, 5000);
                        },
                    }}
                >
                    {shouldStartRender ? (
                        <DomRouter>
                            <DomRoutes>
                                <DomRoute path="/home/home.html" element={<Home />} />
                                <DomRoute path="/home/clause.html" element={<Clause />} />
                                <DomRoute path="/home/loginPhone.html" element={<LoginPhone />} />
                                <DomRoute path="/home/loginPin.html" element={<LoginPin />} />
                                <DomRoute path="/home/signUp-phone.html" element={<SignUpPhone />} />
                                <DomRoute path="/home/signUp-authCode.html" element={<SignUpAuthCode />} />
                                <DomRoute path="/home/signUp-pin.html" element={<SignUpPin />} />
                                <DomRoute path="/home/CheckUserByPin.html" element={<CheckUserByPin />} />

                            </DomRoutes>
                        </DomRouter>
                    ) : null}
                    <KioskAlertError playAudio={playAudio} stopAudio={stopAudio} /> 
                    <KioskAlert {...kioskAlertInfo} />
                </SWRConfig>
            </KioskAlertContext.Provider>
        </>
    );
}



KioskAudio.ts
export const kioskAudio = () => {
    const audio = new Audio();
    const playAudio = (folder: string, file: string) => {
        stopAudio();
        audio.setAttribute('src', `/sounds/${folder}/${file}.mp3`);
        audio.play();
    };
    const stopAudio = () => {
        audio.pause();
        audio.currentTime = 0;
    };
    return {
        playAudio,
        stopAudio,
    };
};



KioskAlertError.tsx
const KioskAlertError: React.FC<IKioskAlertErrorProps> = ({playAudio, stopAudio}) => {
    const [isShow, setIsShow] = useState(false);
    const [content, setContent] = useState('');

    useEffect(() => {
        document.addEventListener('error', (data: CustomEvent) => {
            const message = JSON.parse(data.detail);
            const errorMessage = message.message;

            setContent(getErrorMessage(message.message));
            setIsShow(true);

            switch (errorMessage) {
                case 'Already Registered':
                    console.log('Already joined');
                    playAudio('alert', '2');
                    break;
                case 'Can't find your numbers':
                    console.log('userNotFound');
                    playAudio('alert', '1');
                    break;
            }
        });

        return () => {
            document.removeEventListener('error', null);
        };
    }, []);

    const getErrorMessage = (messageCode) => {
        return messageCode;
    };

    return isShow ? (
        <Alert
            content={content}
            okText={'OK'}
            onOK={() => setIsShow(false)}
            wrapperStyle={defaultWrapperStyle}
            alertStyle={defaultAlertStyle}
            upperSectionStyle={defaultUpperSectionStyle}
            lowerSectionStyle={defaultLowerSectionStyle}
            titleStyle={defaultTitleStyle}
            contentStyle={defaultContentStyle}
            cancelStyle={defaultButtonStyle}
            okStyle={defaultButtonStyle}
        />
    ) : null;
};

export default KioskAlertError;


Solution 1:[1]

As you have used the audio variable inside your audio functions, the reference to the variable in function closures get lost between component re-renders. So you need to convert the kisokAudio util into a custom hook which holds the ref between renders & then use useKioskAudio instead of the simple function.

useKioskAudio.ts

import { useRef } from "react";

export const useKioskAudio = () => {
    const audio = useRef(new Audio());
    const playAudio = (folder: string, file: string) => {
        stopAudio();
        audio.current.setAttribute('src', `/sounds/${folder}/${file}.mp3`);
        audio.current.play();
    };
    const stopAudio = () => {
        audio.current.pause();
        audio.current.currentTime = 0;
    };
    return {
        playAudio,
        stopAudio,
    };
};

and then use it like

const { playAudio, stopAudio } = useKioskAudio();

in your app.tsx component.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Sina