-
Notifications
You must be signed in to change notification settings - Fork 687
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Order confirmation page multi refresh fix #2433
Order confirmation page multi refresh fix #2433
Conversation
|
Performance Test Results The following fails have been reported by WebpageTest. These numbers indicates a possible performance issue with the PR which requires further manual testing to validate. https://pr-2433.pwa-venia.com : LH Performance Expected 0.85 Actual 0.57, LH Best Practices Expected 1 Actual 0.92 |
I am gonna add more tests soon. |
* `cartId`, since the order has been placed and the UI | ||
* will be in the order confirmation step. | ||
*/ | ||
if (cartId && !placedOrder) { |
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.
@tjwiebell had a really good idea in his auth shipping PR. Instead of conditions in effects to invoke lazy queries we can just use the skip
property. The original intent behind the lazy query was to re-fetch data on UI components that are always rendered like the cart trigger. It needed to know if cart id changed so it could re-query for new data.
const {
data: checkoutData
} = useQuery(getCheckoutDetails, {
skip: !cartId,
variables: {
cartId
}
});
However after I mention this I realize that it is currently possible for a user to log out/log in while in cart/checkout (via left drawer) which would require we re-fetch the data. I don't know if this would re-fetch in those cases.
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.
I am fine doing it either way. I like this style as well. Just a small change though, the skip variable value will be !cartId || placeOrderData
.
However after I mention this I realize that it is currently possible for a user to log out/log in while in cart/checkout which would require we re-fetch the data. I don't know if this would re-fetch in those cases.
Ideally speaking it should. Technically speaking, it should since the cartId
changes once you log in or out.
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.
I have tried this and it worked like a charm. When I logged in and out, I could see that the query was being fired.
What do you guys think? Should we stick with this approach?
I say yes.
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.
Did it work when logging in and logging out? The checkout page reflected the user state ie showed authed user cart after logging in and showed empty cart after logging out?
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.
Yeah, it did. Because when we log we use the cartId
associated with the user and the query would pick that up and do its magic. Once the user logs out, a new cartId is created so the query checks and realizes there are not cart items on it. That's my understanding.
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.
Quick point, I have tried both the use cases we are discussing here, useEffect
and skip
, unfortunately, they are a bit diff. I have documented it in this video.
TLDR, useEffect
does not get invoked if the value of the variable changes to the same value like, 123
-> 123
but the skip
variable does not recognize that. It just sees that the value has changed but not the actual value. It does not compare the new value to the old value. When ever the value changes, if that value passes the conditional, the useQuery
will perform its action.
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.
Found the piece of code that is causing the loading screen flash.
The loading screen is controlled by the boolean isLoading
in the useCheckoutPage
talon. This is how the isLoading
is defined:
isLoading: !checkoutCalled || (checkoutCalled && checkoutLoading)
It reads as:
- if the checkout details query is not called, show the loading screen
- if it is called and is loading, show the loading screen
If you pay attention to the 2 screenshots below, in case of useEffect
, checkoutCalled
is not set to true
till it is called, ie. the useEffect
is executed.
In case of useQuery
, it is called when the component is rendered, hence checkoutCalled
is set to true
even on the first render but it is not loading yet. Hence the isLoading
boolean is false first, then when checkoutLoading
is actually set to true, isLoading
is set to true.
I hope this explanation of mine is understandable 🤣
useEffect
skip
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.
Maybe we should revisit the apollo docs for loading states: https://www.apollographql.com/docs/react/data/queries/#inspecting-loading-states
I do wonder, since apollo docs all just seem to use loading
, do we need to check called
? Do the children render before loading
is true?
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.
Pstephen the savior. Using the networkStatus
variable from the useQuery
worked dude. 3bca3e0
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 I be happy that the tests failed to catch the change or sad that I have to update them? 🤔
packages/peregrine/lib/talons/CheckoutPage/__tests__/useCheckoutPage.spec.js
Show resolved
Hide resolved
packages/peregrine/lib/talons/CheckoutPage/__tests__/useCheckoutPage.spec.js
Show resolved
Hide resolved
packages/peregrine/lib/talons/CheckoutPage/__tests__/useCheckoutPage.spec.js
Outdated
Show resolved
Hide resolved
@@ -0,0 +1,209 @@ | |||
import React from 'react'; |
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.
Thank you @revanth0212 for adding this test coverage 🙏 was just about to do the same in scope of my PR, glad I took a peek at this.
…/github.com/magento/pwa-studio into revanth/order_confirmation_multi_reload_fix
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.
I'd really like to see the Apollo network statuses split out for re-use.
*/ | ||
const isLoading = useMemo( | ||
() => | ||
checkoutQueryNetworkStatus ? checkoutQueryNetworkStatus < 7 : true, |
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.
The comment is nice but I'd also like us to have a utility enumeration that exports Apollo's network status codes to make this more readable.
https://github.com/apollographql/apollo-client/blob/master/src/core/networkStatus.ts
This would become something like
checkoutQueryNetworkStatus ? checkoutQueryNetworkStatus < ApolloNetworkStatuses.READY : true
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.
That source file also exposes an isNetworkStatusInFlight
utility function that seems to be performing the same check you are here. Other places in our code could benefit from this as well.
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.
Nice idea @supernova-at. Will do.
I don't think we should export the network status from this talon. It can be something from util or a defaults file. I can do that if that is what you are expecting here.
Also another point, isNetworkStatusInFlight
differs from our implementation. The else case in isNetworkStatusInFlight
returns false but in our case, it has to return true.
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.
Yeah, I was definitely thinking a separate file that this and other talons could use.
|
||
expect(talonProps.isLoading).toBeTruthy(); | ||
|
||
getCheckoutDetailsQueryResult.mockReturnValueOnce({ |
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.
Starting from this line down should be its own separate test. "isLoading should be set to false if the checkout details query networkStatus is greater than or equal to 7".
I'd also test seven (7) explicitly since that's where the errors will likely occur.
}); | ||
}); | ||
|
||
test('isGuestCheckout should be negation of isSignedIn from useUserContext', () => { |
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.
Careful of tests like this, in my opinion this is a little too implementation-specific and therefore brittle.
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.
@supernova-at this is a good point and leads me to believe we should drop the isGuestCheckout
value and just be returning isSignedIn
for use in the UI component.
This raises a question for dev time though. In most of our talons we return some simple boolean states such as loading or render flags such as isSignedIn
. How should we test those?
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.
I wanted to test the fact that it is dependent on isSignedIn
which is from another talon. I wanted to do this because, if someone changes the logic, the test has to fail.
All this is based on my assumptions about testing such booleans. Not sure if that is the right assumption.
@sirugh nice idea. Good topic to discuss.
Reverted |
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.
Looks good! One minor comment but I'll leave it up to you whether you want to change it or not.
const isLoading = useMemo(() => { | ||
const checkoutQueryInFlight = checkoutQueryNetworkStatus | ||
? checkoutQueryNetworkStatus < 7 | ||
: true; |
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.
Since network status is 1-based, here's an alternate approach that reads a little easier in my opinion:
const checkoutQueryInFlight = !checkoutQueryNetworkStatus || checkoutQueryNetworkStatus < 7;
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.
Nice approach Andy, but I would like to stick to the ternary operator approach. In my opinion, in this case, it is easy to read than the ||
approach.
Description
useCheckoutPage
talon has auseEffect
that fetches checkout details everytime thecartId
changes. When the user places an order, a newcartId
will be created because the old one will be purged. Due to this, theuseEffect
was being triggered and in the processisLoading
was changing totrue
. Because of this, the user will see a loading screen for a bit and the order confirmation page will be reloaded. This is un-intended behavior.To fix this, I have added a check to only call the checkout details query if the order has not been placed.
The talon involved in this PR does not have a tests file defined. I need to create and add some tests.
Related Issue
Closes PWA-580
Verification Stakeholders
@sirugh
Verification Steps
Checklist