twilio / twilio-client.js

Twilio’s Programmable Voice JavaScript SDK
https://www.twilio.com/docs/voice/client/javascript
Other
67 stars 32 forks source link

Doesn't work with Next.js #291

Open burstWizard opened 3 years ago

burstWizard commented 3 years ago

This package isn't working with my Next.js application. Whenever I import twilio-client, it gives me one of two errors:

TypeError: Cannot redefine property: instance or ReferenceError: window is not defined

To reproduce this issue,

1) Create a new Next.js application using npx create-next-app 2) Install twilio-client using npm install twilio-client --save 3) Create a page and import twilio-client, using either the CommonJS or ES6 syntax. 4) Visit the page.

simplenotezy commented 3 years ago

Yeah, have the same issue. It doesn't seem to work in Next.js. I get another error though, pretty sure they're related:

index.tsx?a3f0:359 Uncaught     at Function.defineProperty (<anonymous>)
    at Object.<anonymous> (file:///Users/mf/Projects/kople-frontend/node_modules/twilio-client/es5/twilio.js:17:8)
    at Module._compile (node:internal/modules/cjs/loader:1095:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
    at Module.load (node:internal/modules/cjs/loader:975:32)
    at Function.Module._load (node:internal/modules/cjs/loader:816:12)
    at Module.require (node:internal/modules/cjs/loader:999:19)
    at require (node:internal/modules/cjs/helpers:93:18)
    at Object.twilio-client (file:///Users/mf/Projects/kople-frontend/.next/server/pages/projects/[projectId]/users/[userId].js:656:18)
    at __webpack_require__ (file:///Users/mf/Projects/kople-frontend/.next/server/webpack-runtime.js:33:42)
    at eval (webpack-internal:///./hooks/use-calls.js:7:71)
    at Object../hooks/use-calls.js (file:///Users/mf/Projects/kople-frontend/.next/server/pages/projects/[projectId]/users/[userId].js:249:1)
    at __webpack_require__ (file:///Users/mf/Projects/kople-frontend/.next/server/webpack-runtime.js:33:42)
    at eval (webpack-internal:///./components/Timeline/Timeline.js:22:75)
    at Object../components/Timeline/Timeline.js (file:///Users/mf/Projects/kople-frontend/.next/server/pages/projects/[projectId]/users/[userId].js:103:1)
    at __webpack_require__ (file:///Users/mf/Projects/kople-frontend/.next/server/webpack-runtime.js:33:42)

image

burstWizard commented 3 years ago

With some help, I figured out the issue. My errors had to do with twilio-client trying to load on the server-side. So to fix that, you can do something like this

import { useEffect } from  'react';

export  default  function  VoiceRoom(){

  //useEffect doesn't run on the server-side
  useEffect(()=>{

      //for await to work inside useEffect, wrap it in an async function
      async  function  createDevice(){

          const  Device = (await  import('twilio-client')).Device
          const  device = new  Device();

          //For some reason returning the device and then using it doesn't work
          //So keep all the "device.on()" stuff inside the async function

          device.on('incoming', connection  => {
              //Whatever logic you need
          });

      }, [])

      createDevice()

  return(
      <div>
          <p>Hello</p>
      </div>
  )
}
ryan-rowland commented 3 years ago

Thanks for the report! We don't officially support or test with Next.js, but I don't know of any reason why it shouldn't work. @burstWizard thanks for the resolution here; can you confirm whether this is a workaround to get the SDK working (and is something that Twilio could address in the SDK) or the issue was a project configuration issue?

samusgray commented 3 years ago

I was having the same issue and a similar solution works for Nuxt.js with Vue. At first I was importing Device at the top of our actions.js, which worked client side but would throw this same error if run server side, which happens on page refresh or loading the route directly.

Nuxt solution:

In VoipCall.vue (or what ever your component is that starts the call):

  methods: {
    ...mapActions('<yourNameSpaceHere>', [
      'startCall',
    ]),
  },

  async mounted() {
    await this.startCall()
  },

The mounted event does not run server side.

In actions.js:

async startCall() {
  if (typeof window === 'undefined') return;

  const Device = (await import('twilio-client')).Device;

  // do stuff with device
  // ...
}

It would be really nice if there were a server friendly way to import twilio-client, as this is not an issue specific to Nuxt or Next, but rather an issue with twilio-client not gracefully handling being imported server side. It would be awesome if it simply did nothing server side by checking if window is defined.

ryan-rowland commented 3 years ago

Ahh, got it. Yes, this is unfortunate, because the Singleton pattern has been deprecated for a while and is only still included for backward compatibility. In the Voice JS SDK 2.0, the singleton has been removed entirely, so I'd be curious to hear whether you have similar issues using the new API.

Although we don't support 1.x with new features, I've cut an internal ticket for this one as I'd classify this behavior as a bug.