GetStream / stream-chat-react

React Chat SDK ➜ Stream Chat 💬
https://getstream.io/chat/sdk/react/
Other
698 stars 272 forks source link

bug: tutorial breaks when using a token provider #2458

Closed johnmccalla closed 1 month ago

johnmccalla commented 1 month ago

Describe the bug

If you replace the string passed to useCreateChatClient() in your tutorial by a provider, the example fails with Error: You can't use a channel after client.disconnect() was called.

To Reproduce

In line 12 of the tutorial, replace tokenOrProvider: userToken with

tokenOrProvider: () => new Promise((resolve) => setTimeout(resolve, 100)).then(() => userToken)

This is an easy way to simulate how a token provider would behave. I obviously have it failing with my real token server also.

Package version

MartinCupela commented 1 month ago

Hey @johnmccalla I have taken your tokenProvider implementation and plugged it in the attached codesandbox. I could not reproduce the mentioned error.

Could you please provide more info on how to reproduce the issue you are facing?

johnmccalla commented 1 month ago

Hi Martin, thanks for taking the time to try it. Your example helped me figure it out. I had

export default function App() {
  //...
  const client = useCreateChatClient({
    apiKey: process.env.REACT_APP_STREAMIO_APIKEY,
    tokenOrProvider: () => apiGet('/stream/token'),
    userData: { id: userId }
  })
  //...

So, what's happenning is that because the function is being re-run for every render, the token provider function is changing with every call. This is not an abnormal thing for React code, of course, but in the code for useCreateChatClient here the user's connection is reset everytime the function changes. In your tutorial, there's this effect:

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

    const channel = client.channel('messaging', 'custom_channel_id', {
      image: 'https://getstream.io/random_png/?name=react',
      name: 'Talk about React',
      members: [userId]
    })

    setChannel(channel)
  }, [client])

So it's causing an error because client is no longer null, but is often in the disconnected state. The source of the error is pretty unintuitive, imo.

I'd argue that the best implementation would be to not reset the connection on the token function changing, but just let the tokenManager do its thing and get a new token from the provider when/if expires.

Second best would be to document the scoping requirement of the provider.

Cheers!

MartinCupela commented 1 month ago

Hey @johnmccalla , creating a new object on every render is a bad practice documented by the React team. I believe it is not necessary to stress this for the integrators of stream-chat-react. The effect has is designed as intended - the disconnect has to take place as it is expected the dependencies have changed (changed token provider implementation included). If the logic of the token provider changes, the client has to be initiated anew.