-
Notifications
You must be signed in to change notification settings - Fork 1
Description
I've not figured out a solution for this yet, but the following code breaks phoenix.js channel closing logic:
use-phoenix/src/useChannel/useChannel.ts
Lines 172 to 174 in 57e8218
| _channel.off('phx_error'); | |
| _channel.off('phx_close'); | |
| _channel.off('phx_reply'); |
_channel.off discards all registered callbacks, including the internal one from phoenix:
// phoenix channel.js
this.onClose(() => {
this.rejoinTimer.reset()
if(this.socket.hasLogger()) this.socket.log("channel", `close ${this.topic} ${this.joinRef()}`)
this.state = CHANNEL_STATES.closed
this.socket.remove(this)
})Imagine you have a channel that should be joined and left upon chat open/close in UI. If we open & close & open the chat, it should create channel 1, close channel 1, create channel 2 with the same topic (not 1 because 1 should be closed at this point).
The callback above is discarded before phoenix has a chance to call it after channel process termination on server, which causes the closed channel to stay in socket.channels with leaving state instead of being removed from there. This makes findChannel being able to find this "zombie" channel instead of creating a new one -> we attach to "zombie" channel in handleJoin instead of creating a new one.
use-phoenix/src/useChannel/useChannel.ts
Lines 90 to 91 in 57e8218
| const existingChannel = findChannel(socket, topic); | |
| if (existingChannel) return handleJoin(existingChannel); |
I see the reasoning behind these off calls, I don't have the exact repro for the described use case below, but perhaps we could use channel.off(event, ref) instead of channel.off(event) to fix the issue with "error"?
use-phoenix/src/useChannel/useChannel.ts
Lines 144 to 174 in 57e8218
| /* | |
| So the problem is that these .recieve() functions stay persisted even after this hook | |
| has potentially moved on to an entirely different topic. | |
| So consider the following scenario: | |
| - we connect on topic 'room:1' and we error. That is, we aren't permitted to enter. | |
| - Then, using the same hook, we connect to room:2 and we are permitted. | |
| - Since the first one errored, **and the socket has configured rejoin() timeouts**, | |
| the socket will attempt to rejoin 'room:1' even though we have already moved | |
| on to 'room:2'. The rejoin attempt will actually call the recieve functions | |
| and be able to update the state of the hook, even though we are no longer | |
| interested in that topic. | |
| - So, we will be in a success state after joining 'room:2', and then rejoin will | |
| trigger and the recieve('ok') will be called, and since it's for room:1, it will | |
| update the state of the hook back into an error state. | |
| - Here, once the topic changes, we remove all recieve hooks to prevent this from happening. | |
| - So the rejoin() attempt will be called, but this hook won't be listening! | |
| This can be entirely avoided if, the hook-user correctly calls leave() when the `room:1` join | |
| fails, but that's not a guarantee, and I think the expected behavior is that the hook no | |
| longer cares about it's previous topic. | |
| */ | |
| // @ts-ignore | |
| if (_channel.joinPush) _channel.joinPush.recHooks = []; | |
| _channel.off('phx_error'); | |
| _channel.off('phx_close'); | |
| _channel.off('phx_reply'); |
Tbh, I believe the solution with .off/1 or .off/2 on errored channels is not robust and might lead to other issues. I do not have good ideas now, but perhaps it'd be better to find a reasonable way to leave / remove these channels from socket.channels on client side instead of allowing them to stay there forever. I understand that the lib cannot decide if client actually wants this behavior, so maybe it's indeed the job of the client to correctly call leave?