Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'onUserLeft')

I implemented an onUserLeft function on the channel handler and immediately started getting the following error every time it is invoked.

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'onUserLeft')

I am registering the handler inside of a useEffect after the connection with sendbird has been established. The event handler is firing because the desired effect is being observed, but I should not be receiving the error message in the console.

Here is my relevant code:

      const handler = new sb.ChannelHandler()

      handler.onChannelChanged = channel => {
        dispatch(updateChannel(channel.serialize() as Channel))
      }

      handler.onReadReceiptUpdated = channel => {
        dispatch(updateChannel(channel.serialize() as Channel))
      }

      handler.onUserLeft = ({ url }, { userId }) => {
        if (userId === sendbirdUserId) dispatch(removeChannel(url))
      }

      sb.addChannelHandler('CHANNEL_HANDLER', handler)

and the full stack trace of the console error:

SendBird.min.js:6 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'onUserLeft')
    at SendBird.min.js:6:278172
    at Array.forEach (<anonymous>)
    at SendBird.min.js:6:278119
    at SendBird.min.js:6:23275
    at SendBird.min.js:6:412351
    at SendBird.min.js:6:23185
    at new Promise (<anonymous>)
    at le (SendBird.min.js:6:23158)
    at Function.value (SendBird.min.js:6:412325)
    at e.value (SendBird.min.js:6:276705)
    at c.onMessage (SendBird.min.js:6:94301)
    at ws.onmessage (SendBird.min.js:6:88577)

Hi @jkepps,

What version of the JS SDK are you using?

@Tyler i just updated to v3.1.16 which i believe is the latest for v3

i should also note, this only happens for the onUserLeft handler. the other handlers don’t throw any errors when they are invoked.

Hi @jkepps,

Apologizes for the delayed response on this. I took a quick look at trying to reproduce this in codesandbox using JS SDK 3.1.16 but was unable to do so. You can take a look at my code here:

Could you try and reproduce this in a similar way so I can take a look?

Thanks for investigating @Tyler. I attempted to refactor using a similar strategy as your code sandbox, but couldn’t get a connection set up properly which ended up introducing a whole host of other errors.
Here are my relevant files. Maybe you can spot something I’m doing incorrectly…

// src/common/sendbird.ts
export const sb = new Sendbird({ appId: process.env.SENDBIRD_APP_ID })
// src/hooks/useMessaging.ts
import { useEffect } from 'react'
import { logError } from 'src/common/logger'
import { sb } from 'src/common/sendbird'
import { selectSendbirdToken, selectUserId } from 'src/store/selectors/auth'
import { removeChannel, updateChannel } from 'src/store/slices/messaging'
import { updateTotalUnreadMessageCount } from 'src/store/thunks/messaging'
import { useAppDispatch } from 'src/store/useAppDispatch'
import { useAppSelector } from 'src/store/useAppSelector'
import { Channel, ConnectionState } from 'src/types'

export const useMessaging = () => {
  const userId = useAppSelector(selectUserId)
  const sendbirdToken = useAppSelector(selectSendbirdToken)
  const dispatch = useAppDispatch()
  const sendbirdUserId = `dashboard_${userId}`

  useEffect(() => {
    if (!sendbirdToken) return

    const initialize = async () => {
      // do not initialize if the token is set to expire in half a second
      const halfSecondAgo = Date.now() - 500
      if (sendbirdToken.expiresAt < halfSecondAgo) return

      try {
        if (sb.getConnectionState() === ConnectionState.CLOSED)
          await sb.connect(sendbirdUserId, sendbirdToken.token)
        dispatch(updateTotalUnreadMessageCount())
      } catch (error) {
        logError('Failed to connect to sendbird', {
          sendbirdUserId,
          sendbirdToken,
          error,
        })
      }

      const handler = new sb.ChannelHandler()

      handler.onChannelChanged = channel => {
        dispatch(updateChannel(channel.serialize() as Channel))
      }

      handler.onReadReceiptUpdated = channel => {
        dispatch(updateChannel(channel.serialize() as Channel))
      }

      handler.onUserLeft = ({ url }, { userId }) => {
        if (userId === sendbirdUserId) dispatch(removeChannel(url))
      }

      sb.addChannelHandler('CHANNEL_HANDLER', handler)
    }

    initialize()

    return () => {
      sb.removeChannelHandler('CHANNEL_HANDLER')
      sb.disconnect()
    }
  }, [sendbirdToken, sendbirdUserId])
}

this useMessaging hook is being invoked at the top of my routes declarations:

// src/Routes.tsx
export const AppRoutes: React.FC = () => {
  useMessaging()

  return (
    <Routes>
      {/* Unauthenticated Routes */}
      <Route element={<RouteLayout />}>
        <Route path="/login" element={<Login />} />
        <Route path="/forgot-password" element={<ForgotPassword />} />
        <Route path="/reset-password" element={<ResetPassword />} />
      </Route>
     ...
}

@jkepps,

A quick glance over the code you provided show any inherent issues that I can see. I do see you’re utilizing Typescript so I’m going to give that a go and see if there is any issues on the conversion from TS to JS.

I tested this in Typescript as well and didn’t have any issues. Just for the sake of ruling anything funky out, could you get a new instance of SendBird rather than importing sb from your common/sendbird?

It would look like this:

import SendBird from 'sendbird'

// Inside of the initialize useEffect function
const sb = SendBird.getInstance()

I highly doubt that is causing an issue but it’s the only key difference I see between our implementations.

I decided to just spend my efforts upgrading to v4 instead of trying to get rid of this error. However, I’m noticing that there are quite a few types that are not properly exported in v4. For example, the MessageType, SendableMessage, FileMessage, and UserMessage types are all inside of a chat/lib/__definition file that the ts compiler is not able to resolve during compilation. But it seems like we need these types to be able to coerce a message to the correct type so that we can access certain properties…

Also, the sample react app is written in javascript, which isn’t particularly helpful for getting a sense of how you all think the type definitions should be working/used in practice.

Anyway, I know this is not a little off topic to the original purpose of this thread. Let me know if you think I should open a new thread with these concerns. Thanks.

edit: I was looking forward to chatting with you all in the office hours today, but no one showed up. i’m still sitting in the zoom meeting as I’m typing this :confused:

Hey @jkepps,

First and foremost, I apologize that you were in the office hours meeting today and no one showed up. I’ll follow up internally about that and see what is going on.

In regards to the typings. You should be able to access them by importing them as such:

import { MessageType, UserMessage } from '@sendbird/chat/message'

I was able to access them in a codesandbox example without issue.

I’m following up with our Developer Advocacy team about V4 samples in TypeScript. As soon as I have an answer regarding that, I’ll let you know.

I appreciate you looking into what happened to the office hours yesterday. I’m mostly disappointed, I was really looking forward to chatting with some of the team over there.

And yeah, doh, that was definitely not that difficult to figure out for the typings. Thanks for clarifying that.

Please do keep me updated on the typescript sample. I don’t know if that’s something that you guys would be willing to open source, but if you did, I’d happily make some contributions.

I’m running into the same issue when leaving a channel. I haven’t implemented a custom handler.

It looks like for some reason channel is not being destructured properly from action.payload in reducers.js:

Uncaught TypeError: Cannot read properties of undefined (reading 'url')
    at reducer (reducers.js:114:1)
    at updateReducer (react-dom.development.js:16664:1)
    at Object.useReducer (react-dom.development.js:17898:1)
    at useReducer (react.development.js:1626:1)
    at ChannelList (index.jsx:71:1)
    at renderWithHooks (react-dom.development.js:16305:1)
    at updateFunctionComponent (react-dom.development.js:19588:1)
    at beginWork (react-dom.development.js:21601:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)

I’m using sendbird-uikit 2.7.1, and I’m not using any Chat SDKs or APIs directly, except for sign up and getting session tokens on the backend.

Hi @realistic-work-5750,

Welcome to the Sendbird community.

It looks like your use case differs from the the originally reported issue as you’re utilizing the UIKit while the OP was using the Core SDK. I’d kindly ask you create a new topic so we can separate the use cases and approach the investigations separately.

I spoke with our Developer Advocacy teams on the Typescript examples, and it doesn’t look like its immediately on the road map. We just updated our Javascript examples based on the number of people using JS vs TS. If we opt to create one, I’ll be sure to let you know.

In regards to the office hours, someone should be reaching out to you directly soon.

Okay thanks for the update @Tyler.

I’ve got another issue that is blocking my migration to v4 here. Figured it would be better to move the conversation to that thread since it’s a distinctly different issue.