-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix infinite spinner on OldDot transition #5552
Changes from all commits
fe08bb3
807e095
7e04098
ad860ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import Onyx, {withOnyx} from 'react-native-onyx'; | ||
import Str from 'expensify-common/lib/str'; | ||
import moment from 'moment'; | ||
import _ from 'underscore'; | ||
import lodashGet from 'lodash/get'; | ||
|
@@ -33,6 +34,7 @@ import createCustomModalStackNavigator from './createCustomModalStackNavigator'; | |
import Permissions from '../../Permissions'; | ||
import getOperatingSystem from '../../getOperatingSystem'; | ||
import {fetchFreePlanVerifiedBankAccount} from '../../actions/BankAccounts'; | ||
import {removeLeadingForwardSlash} from '../../Url'; | ||
|
||
// Main drawer navigator | ||
import MainDrawerNavigator from './MainDrawerNavigator'; | ||
|
@@ -111,20 +113,12 @@ const modalScreenListeners = { | |
}, | ||
}; | ||
|
||
let hasLoadedPolicies = false; | ||
|
||
/** | ||
* We want to only load policy info if you are in the freePlan beta. | ||
* @param {Array} betas | ||
* @param {String} route | ||
* @returns {Boolean} | ||
*/ | ||
function loadPoliciesBehindBeta(betas) { | ||
// When removing the freePlan beta, simply load the policyList and the policySummaries in componentDidMount(). | ||
// Policy info loading should not be blocked behind the defaultRooms beta alone. | ||
if (!hasLoadedPolicies && (Permissions.canUseFreePlan(betas) || Permissions.canUseDefaultRooms(betas))) { | ||
getPolicyList(); | ||
getPolicySummaries(); | ||
hasLoadedPolicies = true; | ||
} | ||
function isTransitionOrNewWorkspacePage(route) { | ||
return route && !_.any([ROUTES.LOGIN_WITH_SHORT_LIVED_TOKEN, ROUTES.WORKSPACE_NEW], badRoute => Str.startsWith(removeLeadingForwardSlash(route), badRoute)); | ||
} | ||
|
||
const propTypes = { | ||
|
@@ -149,6 +143,8 @@ class AuthScreens extends React.Component { | |
constructor(props) { | ||
super(props); | ||
|
||
this.hasLoadedPolicies = false; | ||
|
||
Timing.start(CONST.TIMING.HOMEPAGE_INITIAL_RENDER); | ||
Timing.start(CONST.TIMING.HOMEPAGE_REPORTS_LOADED); | ||
} | ||
|
@@ -191,7 +187,7 @@ class AuthScreens extends React.Component { | |
UnreadIndicatorUpdater.listenForReportChanges(); | ||
fetchFreePlanVerifiedBankAccount(); | ||
|
||
loadPoliciesBehindBeta(this.props.betas); | ||
this.loadPoliciesBehindBeta(); | ||
|
||
// Refresh the personal details, timezone and betas every 30 minutes | ||
// There is no pusher event that sends updated personal details data yet | ||
|
@@ -235,11 +231,23 @@ class AuthScreens extends React.Component { | |
return true; | ||
} | ||
|
||
/* | ||
* We don't want to unnecessarily re-render this component every time the `currentURL` prop changes, | ||
* but we do want to re-render when we're switching from `transition` or `workspace/new` to any other page. | ||
* Re-rendering in that case will re-trigger `componentDidUpdate` and `loadPoliciesBehindBeta`, | ||
* which we only want to do after we're done with the `transition` and `workspace/new` pages. | ||
* If we don't wait to load the policies until after we're done with those pages, | ||
* we may accidentally overwrite the newly-created policy and land on an infinite loading spinner. | ||
*/ | ||
if (isTransitionOrNewWorkspacePage(this.props.currentURL) && !isTransitionOrNewWorkspacePage(nextProps.currentURL)) { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
componentDidUpdate() { | ||
loadPoliciesBehindBeta(this.props.betas); | ||
this.loadPoliciesBehindBeta(); | ||
} | ||
|
||
componentWillUnmount() { | ||
|
@@ -252,7 +260,26 @@ class AuthScreens extends React.Component { | |
NetworkConnection.stopListeningForReconnect(); | ||
clearInterval(this.interval); | ||
this.interval = null; | ||
hasLoadedPolicies = false; | ||
this.hasLoadedPolicies = false; | ||
} | ||
|
||
/** | ||
* We want to only load policy info if: | ||
* - We are on the free plan beta | ||
* - We are not on the transition or new-workspace pages (if we load policies while creating a new one we may accidentally overwrite the new policy due to race condition) | ||
* - We have not already loaded policies | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we just keep calling this method anytime the component updates until all the conditions are met? Seems fine but also kind of inside out to me. This might be off base, but I feel like we are starting to get into some territory where Onyx's async philosophy is hurting us in the code readability department. Would there be any worry about a race condition or even a need to explain it if we could have something like... const promises = [getBetas()];
if (shouldCreateWorkspace) {
promises.push(createPolicy());
}
Promise.all(promises)
.then(() => {
getPolicyList();
getPolicySummaries();
}); Not sure if we can actually do this or where this logic would run. We can't do it in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I agree, and was suggesting something similar in this thread. In cases when we're just displaying a full-screen loading indicator while waiting for things to happen, our Onyx async philosophy doesn't seem to serve us well/leads to overly-complicated code. I think that this whole flow could be simplified by just consolidating everything into a single Thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved this conversation here. |
||
*/ | ||
loadPoliciesBehindBeta() { | ||
// Do not load policies if we're not in the correct betas or if we're in the process of creating a new policy | ||
if (this.hasLoadedPolicies | ||
|| isTransitionOrNewWorkspacePage(this.props.currentURL) | ||
|| !Permissions.canUseFreePlan(this.props.betas) | ||
|| !Permissions.canUseDefaultRooms(this.props.betas)) { | ||
return; | ||
} | ||
getPolicyList(); | ||
getPolicySummaries(); | ||
this.hasLoadedPolicies = true; | ||
} | ||
|
||
render() { | ||
|
@@ -459,5 +486,8 @@ export default compose( | |
betas: { | ||
key: ONYXKEYS.BETAS, | ||
}, | ||
currentURL: { | ||
key: ONYXKEYS.CURRENT_URL, | ||
}, | ||
}), | ||
)(AuthScreens); |
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.
Should we add some comments here to explain this check? I see that we want to prevent updating
AuthScreens
when moving from/transition
or/workspace/new
to another route, but unsure why?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.
Well, my reasoning here was since I added the
currentURL
Onyx key, that will change any time we navigate anywhere:And by default we probably don't want to re-render the whole component tree from
AuthScreens
down every time the route changes. The only time we do want to force a re-render and triggercomponentDidUpdate
is when we are moving fromtransition
orworkspace/new
to anywhere else, because that means it's now safe to load policies without risking overwriting a newly-created policy. Overall this design doesn't feel great, but I think it works.If that all makes sense, I could add a comment like this:
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.
Another way we could maybe simplify this code without breaking the E/App philosophy would be to trigger
this.loadPoliciesBehindBeta
directly here inshouldComponentUpdate
, but in general I'm hesitant to put side-effects inshouldComponentUpdate
. Not sure I can explain why exactly, it just feels like a bad pattern. 馃し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.
+1 to leaving the above comment for an explanation but I also think it could use a bit of re-working and additional lines to help with clarity. e.g.