twilio-video.js icon indicating copy to clipboard operation
twilio-video.js copied to clipboard

Uncaught TypeError: Class extends value [object Object] is not a constructor or null in React Remix

Open mason-t3h opened this issue 1 year ago • 3 comments

Hi, I am getting the below error when I try to use twilio-video in my React Remix app, as far as I know the error shows when it is running imports, how can I fix this?

Uncaught TypeError: Class extends value [object Object] is not a constructor or null at mediasignaling.js:1:1 at mediasignaling.js:7:30 at node_modules/twilio-video/es5/signaling/v2/mediasignaling.js (mediasignaling.js:76:1) at __require2 (chunk-WX2OHN6X.js:18:50) at node_modules/twilio-video/es5/signaling/v2/dominantspeakersignaling.js (dominantspeakersignaling.js:3:24) at __require2 (chunk-WX2OHN6X.js:18:50) at node_modules/twilio-video/es5/signaling/v2/room.js (room.js:4:34) at __require2 (chunk-WX2OHN6X.js:18:50) at node_modules/twilio-video/es5/signaling/v2/cancelableroomsignalingpromise.js (cancelableroomsignalingpromise.js:5:23) at __require2 (chunk-WX2OHN6X.js:18:50)

I used const Video = require('twilio-video') and import * as Video from 'twilio-video' both are not working

Here is my tsconfig.json

"compilerOptions": { "lib": [ "DOM", "DOM.Iterable", "ES2019" ], "isolatedModules": true, "esModuleInterop": true, "jsx": "react-jsx", "moduleResolution": "node", "resolveJsonModule": true, "target": "ES2019", "strict": true, "allowJs": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".",

mason-t3h avatar Jul 07 '23 05:07 mason-t3h

I ran into the same issue, but was unable to fix it via imports or config. I ended up instead importing the CDN version during page load and ensuring it only ran in client side.

So, on the routes I want to use Twilio, I added:

export const handle = {
    scripts: () => [
        {
            src: "//sdk.twilio.com/js/video/releases/2.27.0/twilio-video.min.js",
        },
    ],
};

Then, in my root.tsx file, I use the <ExternalScripts /> component from remix-utils which looks into the handle for each route.

In the components themselves, I ended up wrapping any video components in the <ClientOnly> component from the same remix-utils package to ensure it's browser side only. Like this:

<ClientOnly fallback={<Text>Please wait....</Text>}>
    {() => (
        <MyVideoComponent />
    )}
</ClientOnly>

Lastly - to get type defs, I wrote a super hacky, function like this:

/* eslint-disable @typescript-eslint/consistent-type-imports */
export function getBrowserTwilio() {
    if (typeof document === "undefined") {
        // Server should just not do anything. Wrapping all the stuff in <ClientOnly> should ensure that
        return {} as typeof import("twilio-video");
    }
    return (window as any).Twilio.Video as typeof import("twilio-video");
}

When I call getBrowserTwilio - I get an object which returns the Twilio.Video object on the window. It works, but certainly not ideal. If we end up continuing with Twilio, we'll likely dig deeper and hopefully find a cleaner option.

idreaminteractive avatar Jul 11 '23 20:07 idreaminteractive

@mason-t3h ,

Thanks for writing in. Can I have access to your repository so that I can try a few things on my local dev machine?

manjeshbhargav avatar Jul 25 '23 20:07 manjeshbhargav

Also having this issue, running @remix-run/dev 1.19.1

yemi avatar Aug 02 '23 10:08 yemi