Skip to content
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

Patch: "Reloading the registration page should warn about data loss" #8377

Merged
merged 15 commits into from
Apr 29, 2022
Merged
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/components/structures/auth/Registration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ export default class Registration extends React.Component<IProps, IState> {
this.replaceClient(this.props.serverConfig);
}

componentDidUpdate(_, prevState: IState) {
if (prevState.doingUIAuth !== this.state.doingUIAuth) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could still register twice, once on doingUIAuth false->true and once back (and if it keeps toggling then it would keep going

One thing to note (which I think is stupid) about addEventListener, is it does not check if the listener is already bound & removeEventListener only removes one instance, not all instances. So this code really needs to be solid because otherwise you'll leave stray listeners hanging around which will keep the warning even much later into the app lifecycle

Copy link
Contributor Author

@yaya-usman yaya-usman Apr 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure, i really understand your first statement clearly, but as for removeEventListener, when the component unmounts doesn't it remove all instances? since the listeners is only tied to one component, i guess it should be scoped within that component, this is the first time i am knowing about the one instance thing of listeners based on your text. So what do you suggest i do?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you call addEventListener 5 times, 5 handlers will be registered (even if the same method is passed) - removeEventListener only removes one of them, causing a memory leak by keeping a pointer to this component instance around.

Maybe easier would be to register the handler always in componentDidMount, but do the if doingUIAuth check inside the handler

Copy link
Contributor Author

@yaya-usman yaya-usman Apr 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry to ask again, i am finding it rather confusing that addEventListener will be registered as many times the method runs so that means the if statement is not doing anything then cause it was supposed to only register the listener if there is a disparity between the prevState and the current State. Also if i placed the listener in componentDidMount, then that means , i 'll just put this.state.doingUIAuth as the if condition since there is no prevState

Copy link
Contributor Author

@yaya-usman yaya-usman Apr 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i realized this.state.doingUIAuth will always be false, so the listener will never be registered. if i placed it in the componentDidMount

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there is a disparity between the prevState and the current State

Right but that disparity could happen multiple times within a single mounting of this component, e.g if you start registration, do captcha, then finish captcha doingUIAuth would go false->true->false then unmount

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i 'll just put this.state.doingUIAuth as the if condition since there is no prevState

Precisely

Copy link
Contributor Author

@yaya-usman yaya-usman Apr 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i realized this.state.doingUIAuth will always be false, so the listener will never be registered. if i placed it in the componentDidMount

componentDidMount only mounts once and doingUIAuth will be false, so there is no way it'll register the listener.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, always register the handler, then inside unloadCallback you can check doingUIAuth

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

//triggers a confirmation dialog for data loss before page unloads/refreshes
window.addEventListener("beforeunload", this.unloadCallback);
}
}

componentWillUnmount() {
window.removeEventListener("beforeunload", this.unloadCallback);
}

private unloadCallback = (event: BeforeUnloadEvent) => {
event.preventDefault();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will always show the confirmation dialog

According to the specification, to show the confirmation dialog an event handler should call preventDefault() on the event.

(Empirically only in Firefox & Safari)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's now working as it should. I have tested it in the three browsers.

event.returnValue = "";
return "";
};
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
// eslint-disable-next-line
UNSAFE_componentWillReceiveProps(newProps) {
Expand Down