-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
Initialise pusher before rendering children #54188
Initialise pusher before rendering children #54188
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks a bit like a workaround to me. Can you show where this was crashing due to the initialization being too late? why does this only happen for guides? Would be nice to have more details about the root cause
@@ -260,6 +260,16 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie | |||
}; | |||
}, [theme]); | |||
|
|||
const pusherInitialized = useRef(false); | |||
|
|||
// eslint-disable-next-line react-compiler/react-compiler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we disabling eslint
here?
@aldo-expensify the primary issue with Pusher was that it was initialized asynchronously outside of the React context. Previously, the app would crash when the ReportScreen was accessed directly via a deep link, as discussed here. This problem arose due to a regression introduced in this PR , where I removed an additional state used for conditionally rendering the ActiveGuidesEventListener, forgetting that To resolve this, the initialization of Pusher now occurs before the rendering of any child components within AuthScreens, thereby mitigating the issue. In previous PR I altered the logic of subscribing to Pusher. This update involves maintaining a reference to a Promise that initializes Pusher, where new subscriptions chain onto this promise. As a result, the additional state in AuthScreens is no longer necessary, which reduces re-renders and simplifies the handling of Pusher initialization across the app. This approach ensures that the state of Pusher's initialization does not need to be tracked separately in various components. |
@jnowakow thank you very much for all the details. Just a NAB: Usually code that runs during rendering should be just that: rendering code. What about adding a wrapper component like:
Not sure if it introduced other problems though. |
Yeah but in this case it would cause screen to blink as we would wait with rendering whole app before Pusher initialises. So it's not a case here. But I think I have a better solution. I'll update the PR shortly |
@aldo-expensify now it works as it should. All subscription logic is handled by Pusher and React components don't care if its initialisation state. Earlier I chained on the |
src/libs/Pusher/pusher.ts
Outdated
@@ -90,8 +93,9 @@ function callSocketEventCallbacks(eventName: SocketEventName, data?: EventCallba | |||
* @returns resolves when Pusher has connected | |||
*/ | |||
function init(args: Args, params?: unknown): Promise<void> { | |||
initPromise = new Promise((resolve) => { | |||
return new Promise((resolve) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this strategy of preserving the original promise work fine if the user does this:
- Log in with
Account A
- Log out
- Log in with
Account B
?
I imagine that before a new promise would be created, now we reuse the same. Could that cause a bug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! It could lead to this sequence and crash:
User A
logs ininitPromise
is resolvedUser A
logs outsocket
is reassigned tonull
User B
logs ininitPromise
is resolved butsocket
is not created yet- App crashes here
Lines 249 to 250 in 31adaa8
throw new Error(`[Pusher] instance not found. Pusher.subscribe() most likely has been called before Pusher.init()`);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But we can avoid this by creating new Promise
in disconnect
function like that:
/**
* Disconnect from Pusher
*/
function disconnect() {
if (!socket) {
Log.info('[Pusher] Attempting to disconnect from Pusher before initialisation has occurred, ignoring.');
return;
}
socket.disconnect();
socket = null;
pusherSocketID = '';
initPromise = new Promise((resolve) => {
resolveInitPromise = resolve;
});
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! That would cause a bug as now if you call pusher.subscribe()
it will fail with [Pusher] instance not found
, socket is cleared on logout and not created again since the promise is already resolved.
Actually the bug already exists on main too as we can always call subscribe
before init
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Github comments are not loading up in time 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@s77rt yeah, github lags sometimes but it seems that we all agree to the solution :D
Reviewer Checklist
Screenshots/VideosAndroid: NativeAndroid: mWeb ChromeiOS: NativeiOS: mWeb SafariMacOS: Chrome / Safariweb.movMacOS: Desktop |
@jnowakow Can you please change the tests section to the below: Tests
Offline tests QA Steps
|
@s77rt done 🫡 |
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
Thank you for handling! |
🚀 Deployed to staging by https://github.com/aldo-expensify in version: 9.0.77-0 🚀
|
🚀 Deployed to production by https://github.com/jasperhuangg in version: 9.0.77-6 🚀
|
Explanation of Change
This change starts Pusher initialisation before any children of
AuthScreens
is rendered allowing them to call safelysubscribe
. When initialisation was triggered inuseEffect
it was run after children was rendered leading to error. Now initialisation is triggered during first render and error doesn't happen.Fixed Issues
$ #54142
PROPOSAL: N/A
Tests
Offline tests
N/A
QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
PR Author Checklist
### Fixed Issues
section aboveTests
sectionOffline steps
sectionQA steps
sectiontoggleReport
and notonIconClick
)src/languages/*
files and using the translation methodSTYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)Design
label and/or tagged@Expensify/design
so the design team can review the changes.ScrollView
component to make it scrollable when more elements are added to the page.main
branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTest
steps.Screenshots/Videos
Android: Native
N/A
Android: mWeb Chrome
N/A
iOS: Native
N/A
iOS: mWeb Safari
N/A
MacOS: Chrome / Safari
web.mov
MacOS: Desktop
N/A