ably / ably-js

Javascript, Node, Typescript, React, React Native client library SDK for Ably realtime messaging service
https://ably.com/download
Apache License 2.0
309 stars 55 forks source link

What's the purpose of ChannelProvider in addition to the useChannel hook? #1786

Open chdsbd opened 3 months ago

chdsbd commented 3 months ago

The requirement to use ChannelProvider in Ably-js v2 is kind of annoying because you need to ensure you couple each call to useChannel with a corresponding ChannelProvider.

What's the technical reason ChannelProvider is required? Could we pass all the required information into the hook?

┆Issue is synchronized with this Jira Task by Unito

VeskeR commented 2 months ago

Hi @chdsbd !

The ChannelProvider component was introduced to avoid issues with inconsistent channel configurations and potential errors arising from them. In ably-js v1, using the useChannel or usePresence hooks with the options parameter could lead to errors when using hooks with the same channel name but different options, or when attempting to dynamically change options for a channel. Here are some older issues related to this problem that I found quickly: https://github.com/ably/ably-js/issues/1570, https://github.com/ably/ably-js/issues/1477.

For example, code setups like this would fail in v1:

// App.js
export const App = () => {
  return (
    <AblyProvider client={client}>
      <ConnectedUsers />
      <LastMessage />
    </AblyProvider>
  )
}

// ConnectedUsers.js
export const ConnectedUsers = () => {
  // usePresence creates channel without options
  const { presenceData, updateStatus } = usePresence("my-channel");
  return (
    <div>
      {presenceData.map((msg, index) => <li key={index}>{msg.clientId}: {msg.data}</li>)}
    </div>
  )
}

// LastMessage.js
export const LastMessage = () => {
  const [lastMessage, setLastMessage] = useState();
  // `useChannel` here uses the same channel name but tries to set channel options, which would throw an error because this channel has already been created without options
  useChannel({ channelName: "my-channel", options: { params: { rewind: '1' } } }, (message) => {
    setLastMessage(message);
  });
  return (
    <div>
      {lastMessage}
    </div>
  )
}

Running this code would throw Error: Channels.get() cannot be used to set channel options that would cause the channel to reattach. Please, use RealtimeChannel.setOptions() instead. due to internal rules on how channels should communicate with backend Realtime services and handle channel option changes.

So, passing options directly in hooks would cause channels to reattach incorrectly or lead to errors based on the order of calls. The ChannelProvider centralizes the definition of channels and their options so they are managed correctly.