-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Run subscriber immediately #23
Comments
You are right we get a render before the busReducer is subscribed to the event bus but everything I have read about React hooks suggests to do this. We should not run sideeffects in render which is what calling I did notice a bug now you bring this up. The following: function defaultSubscriber<E extends BusEvent>(
dispatch: DispatchFn<E>,
bus: EventBus
) {
bus.subscribe<E>("**", dispatch);
} Should probably be: function defaultSubscriber<E extends BusEvent>(
dispatch: DispatchFn<E>,
bus: EventBus
) {
return bus.subscribe<E>("**", dispatch);
} The idea being that: // Run the subscriber
useEffect(() => subscriber(dispatch, bus), [subscriber, dispatch, bus]); Will actually unsubscribe every time the dependencies change. Perhaps I will write a test for this later this week and fix it. |
@dmicic Can you share a repo demonstrating the missing events? Maybe there are other ways to gather missing events and ensure they are replayed correctly. In my head I am thinking of passing a special bus to collect events and then playing them back once subscribed or pausing events and queueing events until the bus is subscribed |
@ryardley Please have a look at this code here: https://codesandbox.io/s/focused-swirles-2hbms TLTR: Yes, I fully agree with your comments and I believe some kind of queuing mechanism would be good enough to solve my issue and at same time, still be 'hooks compliant'. You can look at this problem from different perspective. The first one, and probably the most important one, is what you describe in regards to the correct implementation with react hooks.
|
@ryardley Any thoughts on this issue? I think a queuing solution could be prone to error. How long does the events need to be queued? How does the bus now whether events have to be pushed to newly subscribed reducer? (maybe the events happened in the passed, not during the actual render stage. In that case, new subscribes must not receive "old" events). Hence, bus must now when to start and stop recording the events. Maybe there is a simpler solution to that problem by changing the behavior in the bus. The subscribe method of the eventbus should initially remove the handler (if it exists) and then (re-)subscribe again. const type = getEventType(eventType);
// Remove first if it exists already.
this.emitter.removeListener(type, handler);
this.emitter.on(type, handler);
return () => this.emitter.off(type, handler); The useBusReducer could be changed to: const unsub = subscriber(dispatch, bus);
useEffect(() => unsub, [subscriber, dispatch, bus]); Assuming the dispatch is memoized in this situation, the code would not create multiple subscriptions, even if it function gets executed multiple times. Doesn't look like a perfect solution... But so far, all tests are still green :) |
I am a little fuzzy as to what you mean here regarding subscription/unsubscription and how that helps. I am also travelling and away from a computer at the moment so I imagine once I play around with a potential fix this might make sense to me. What I meant to do that I haven’t had a chance to here would be to create a PR with a test case based on your example code that tests for the error and demonstrates the failing case quickly followed by implementing a fix. I have felt this an edge case that requires a complex fix so it is a little lower on the priority list in my head but if it’s urgent for you then let’s try and get this done? |
Ok I have a failing test case here: https://github.com/ryardley/ts-bus/tree/fix-missing-events-during-render if I get time this afternoon I might be able to look at the solutions you suggest here. |
So the fix appears simple which is to change |
This is now live |
The subscriber function should run immediately when the useBusReducer method gets called, otherwise it could lead to lost events.
useEffect gets triggered only after component has been rendered.
ts-bus/src/useBusReducer.ts
Line 30 in 23de6fb
Example:
The useBusReducer should call the subscriber method immediately and only once.
Pseudo-code solution:
I am not aware of a more elegant solution without the useState hook... :/
Running code like the creation of subscriptions during the render phase may not follow React's best practises, however, it would still make life easier in some cases and it's up to the developer not to missuse it :)
The text was updated successfully, but these errors were encountered: