ably-js icon indicating copy to clipboard operation
ably-js copied to clipboard

usePresence throws unhandled error

Open danielrhodes opened this issue 1 year ago • 1 comments

Currently I have a function inside a hook like the following:

  const {presenceData, updateStatus} = usePresence<PresenceDataType>(
    channelName,
    {/* data */},
  );

  const startTyping = useCallback(() => {
    try {
      updateStatus({/* data */});
    } catch (e: any) {
        // Not caught here?
    }
  }, [updateStatus]);

and I am seeing the following unhandled error in production:

Error Unable to update presence channel while in suspended state 
    (native) call
    node_modules/ably/build/ably-reactnative.js:1187:32 PartialErrorInfo
    node_modules/ably/build/ably-reactnative.js:12482:59 _enterOrUpdateClient
    node_modules/ably/build/ably-reactnative.js:2924:21 encode
    node_modules/ably/build/ably-reactnative.js:12462:41 _enterOrUpdateClient
    (native) apply
    node_modules/ably/build/ably-reactnative.js:877:25 anonymous
    /Users/distiller/react-native/packages/react-native/sdks/hermes/build_iphoneos/lib/InternalBytecode/InternalBytecode.js:61:9 tryCallTwo
    /Users/distiller/react-native/packages/react-native/sdks/hermes/build_iphoneos/lib/InternalBytecode/InternalBytecode.js:216:25 doResolve
    /Users/distiller/react-native/packages/react-native/sdks/hermes/build_iphoneos/lib/InternalBytecode/InternalBytecode.js:82:14 Promise
    node_modules/ably/build/ably-reactnative.js:876:23 promisify
    node_modules/ably/build/ably-reactnative.js:12443:43 _enterOrUpdateClient
    node_modules/ably/build/ably-reactnative.js:12426:41 update
    node_modules/ably/react/cjs/hooks/usePresence.js:70:36 usePresence
    src/hooks/room/use-room-presence.ts:20:19 anonymous
    src/components/room/Base/index.tsx:318:22 ?anon_0_
    (native) next
    node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:24 asyncGeneratorStep
    node_modules/@babel/runtime/helpers/asyncToGenerator.js:22:27 _next
    node_modules/@babel/runtime/helpers/asyncToGenerator.js:27:12 anonymous
    /Users/distiller/react-native/packages/react-native/sdks/hermes/build_iphoneos/lib/InternalBytecode/InternalBytecode.js:61:9 tryCallTwo
    /Users/distiller/react-native/packages/react-native/sdks/hermes/build_iphoneos/lib/InternalBytecode/InternalBytecode.js:216:25 doResolve
    /Users/distiller/react-native/packages/react-native/sdks/hermes/build_iphoneos/lib/InternalBytecode/InternalBytecode.js:82:14 Promise
    node_modules/@babel/runtime/helpers/asyncToGenerator.js:19:23 anonymous
    src/components/room/InputBar/index.tsx:132:17 anonymous
    src/components/room/InputBar/index.tsx:198:20 anonymous
    node_modules/react-native/Libraries/Components/TextInput/TextInput.js:1281:45 _onChange
    (native) apply
    node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:22:15 invokeGuardedCallbackImpl
    (native) apply
    node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:40:34 invokeGuardedCallback
    (native) apply
    node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:53:30 invokeGuardedCallbackAndCatchFirstError
    node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:73:42 executeDispatch
    node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1125:24 executeDispatchesAndReleaseTopLevel
    (native) call
    node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:361:63 forEachAccumulated
    node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1161:8 anonymous
    node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:8457:14 batchedUpdatesImpl
    node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1106:30 batchedUpdates
    node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1137:17 _receiveRootNodeIDEvent
    node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1178:28 receiveEvent
    (native) apply
    node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:433:34 __callFunction
    node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:113:26 anonymous
    node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:368:11 __guard
    node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:112:17 callFunctionReturnFlushedQueue

As a side note: I've seen this library throwing other types of exceptions (e.g. in useAbly) when there is no client. Throwing exceptions inside a hook is not normal practice in React (it causes a white screen of death). Same for throwing any sort of exception when it cannot be handled by the caller.

In this case, updateStatus should ideally be a Promise and resolve/reject.

┆Issue is synchronized with this Jira Bug by Unito

danielrhodes avatar Feb 07 '24 09:02 danielrhodes

Hey @danielrhodes,

Thanks for reporting this! updateStatus is actually an asynchronous function, but we don't return a Promise right now. We will fix this. In the meantime, I recommend looking at connectionError and channelError before calling updateStatus to prevent this error. Alternatively, you can use a direct API call:

  const { presenceData, updateStatus } = usePresence<PresenceDataType>(
    channelName,
    {/* data */},
  );
  const { channel } = useChannel(channelName);

  const startTyping = useCallback(async () => {
    try {
      await channel.presence.update({/* data */});
    } catch (e: any) {
        // catch here
    }
  }, [channel.presence]);

ttypic avatar Feb 07 '24 11:02 ttypic

updateStatus returned by the usePresence hook has been updated to return Promise and be async. This change will be included in the next minor release for ably-js

VeskeR avatar May 29 '24 14:05 VeskeR