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

Fix: Router white flash on first navigation to page #3772

Merged
merged 18 commits into from
Dec 21, 2021

Conversation

Tobbe
Copy link
Member

@Tobbe Tobbe commented Nov 22, 2021

This is an alternative attempt at a solution for #2124

When I was reviewing @callingmedic911's PR for the same issue (#3767) I did some refactoring while getting myself up-to-speed on the code again. And while doing so (and after reading @jtoar's excellent link to the React beta docs) I came up with a (in my eyes) simpler solution to the problem.

This PR has two commits (as of writing this). The first one is just the refactoring I did. The second one is the actual solution to the linked issue.

The TL;DR of it is: Set a hard-coded key on the <Route>s so that <PageLoader> can keep its state while navigating.

Need to update the docs: https://redwoodjs.com/docs/prerender#flash-after-page-load

@callingmedic911
Copy link
Member

@Tobbe I checked-out this locally, this is a neat solution! But I'm afraid unmounting could still happen if navigation is not between the Route to Route. It could be from the old Route to a Set which contains the new route. Here's a small repoduction:

<Router>
  <Set wrap={TestWrapper}>
    <Route path="/second" page={SecondPage} name="second" />
  </Set>
  <Route path="/first" page={FirstPage} name="first" />
  <Route notfound page={NotFoundPage} />
</Router>

From what I understood, if it was only possible to wrap Set once, even we could have handled it with simple control flow but Set could be multi-level nested. Can we somehow retain the route state even if parents change?

@thedavidprice thedavidprice removed their assignment Nov 22, 2021
@thedavidprice thedavidprice marked this pull request as draft November 22, 2021 17:52
@Tobbe
Copy link
Member Author

Tobbe commented Nov 22, 2021

It could be from the old Route to a Set which contains the new route

Yes, I considered this too.

Here's what I'm afraid will happen if we have a "global" Page. But I haven't fully verified.

The PageLoader is only for the actual page, not the Set and the wrappers around it.
So if you jump from a page in BlogLayout to a page in AdminLayout you'd get the old page rendered inside AdminLayout until pageLoadingDelay ms has passed, or the new page has finished downloading. I thought this would be weird, so I made sure everything was unmounted as soon as you "leave" the current Set

@callingmedic911
Copy link
Member

The PageLoader is only for the actual page, not the Set and the wrappers around it. So if you jump from a page in BlogLayout to a page in AdminLayout you'd get the old page rendered inside AdminLayout until pageLoadingDelay ms has passed, or the new page has finished downloading. I thought this would be weird, so I made sure everything was unmounted as soon as you "leave" the current Set

You're right, that would be a very inconsistent state. Worse than the white screen IMO.

Yes, I considered this too.

I see the white page for this case, am I missing something here?

@Tobbe
Copy link
Member Author

Tobbe commented Nov 22, 2021

I see the white page for this case, am I missing something here?

Sorry, English is difficult... I just meant I knew about the scenario you mentioned. And that I thought the white flash was better than the alternative I described.

@callingmedic911
Copy link
Member

Oh, I see. Would love to know your thought on this: #3767 (comment)
Specifically:

Long term fix can be a bit of refactoring. Theoretically, we have to put Loader outside of Route/activeChildren and pass download page module as a prop to the current Route. Then instead of returning PageLoader from InternalRoute, it can return Page directly. But this seemed too much code change considering we are very near to v1. However, if we agree to it I can proceed with guidance from you guys - @Tobbe @jtoar.

Having PageLoader outside will hold the "swapping" process (including Sets) until the page is ready. What do you think? Too much change?

@Tobbe Tobbe force-pushed the tobbe-router-white-flash branch 2 times, most recently from 737863d to e3e5951 Compare November 22, 2021 23:26
@Tobbe
Copy link
Member Author

Tobbe commented Nov 23, 2021

Having PageLoader outside will hold the "swapping" process (including Sets) until the page is ready. What do you think? Too much change?

I felt I couldn't get a good grip on how much work this would be, so I just got started on it. And it doesn't look like it'll be too bad. I'll spend a few hours more on it tomorrow to finish it off.

@Tobbe Tobbe force-pushed the tobbe-router-white-flash branch 2 times, most recently from 7831b05 to 49f0077 Compare November 26, 2021 14:53
@Tobbe
Copy link
Member Author

Tobbe commented Nov 26, 2021

it doesn't look like it'll be too bad. I'll spend a few hours more on it tomorrow to finish it off.

Hah! That wasn't true. This turned out to be a much bigger and complicated change than I first thought.


@jtoar This touches on the a11y stuff you've built. Had to update the Set test snapshots. Can you please take a look?
@dac09 pre-rendering died due to my changes. I've gotten them back to life, but please do take a look. How much confidence do you have in our pre-render tests?

Copy link
Contributor

dac09 commented Nov 26, 2021

Prerender tests are pretty solid!

Copy link
Member Author

Tobbe commented Nov 26, 2021

Awesome! That's a relief 🙂

@Tobbe Tobbe marked this pull request as ready for review November 26, 2021 18:52
@Tobbe
Copy link
Member Author

Tobbe commented Nov 26, 2021

@callingmedic911 I'd love your review of this 🙏

@callingmedic911
Copy link
Member

@callingmedic911 I'd love your review of this 🙏

Glad to. Huge props to you for pulling this big of a change this fast. 🙌 🚀 I'll try to squeeze some time this weekend to review.

Copy link
Contributor

@jtoar jtoar left a comment

Choose a reason for hiding this comment

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

Awesome job @Tobbe! This one's a doozy. I'll have to take more than one pass at reviewing it.

First off, super happy that ActiveRouteLoader is a function component! When I was digging into React 18 I was worried that it might take a lot of nontrivial work to get the Router concurrent-mode/suspense ready. There's still some work to do but I'm feeling a lot better about it now that it's a function component.

I've tried this PR out locally on an app I have. The behavior's definitely fixed, and I haven't caught any regressions yet.

After this PR, I think we need to take some time to organize some of the code in the router package. For example, path-validation logic seems to be duplicated in a few places; it'd be easier to follow if it was centralized. Moreover, we need a legit "understanding the router" or contributing guide.

Comment on lines 201 to 165
if (global.__REDWOOD__PRERENDERING) {
// babel auto-loader plugin uses withStaticImport in prerender mode
// override the types for this condition
const syncPageLoader = spec.loader as unknown as synchronousLoaderSpec
const PageFromLoader = syncPageLoader().default

const prerenderLoadingState: LoadingStateRecord = {
[path]: {
state: 'DONE',
page: PageFromLoader,
},
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It's tempting to want to move this up and "return early", but (I think) it actually needs to be here because we can't call hooks conditionally. That's all just to say that it may be worth adding a comment here explaining why it's last.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done. Please lmk what you think

Copy link
Member Author

Choose a reason for hiding this comment

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

@jtoar Have you had a chance to look at this yet?

@Tobbe Tobbe force-pushed the tobbe-router-white-flash branch 3 times, most recently from 15b1a3a to e20e26b Compare November 30, 2021 05:00
@Tobbe
Copy link
Member Author

Tobbe commented Nov 30, 2021

The CI jobs keeps failing to install. Not sure how to get this unstuck
It was a broken yarn.lock file. Now fixed

Copy link
Contributor

@jtoar jtoar left a comment

Choose a reason for hiding this comment

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

This looks good to go! I feel like this really opens the door to even more refactoring, like you brought up in your page loader issue (I think we could consolidate “validatePaths” too—I’ll make a few follow-up issues myself).

I’m really impressed by all the work you did with the router tests. I thought that we might have to be perennially on-the-look-out for the flash, but you’ve seriously tested it every which way. I’ll try to take a page out of your book when writing tests going forward.

I’ll leave you to merge 🚀

@Tobbe Tobbe force-pushed the tobbe-router-white-flash branch from 0ffc1df to 3d6610b Compare December 21, 2021 04:58
@Tobbe Tobbe enabled auto-merge (squash) December 21, 2021 04:59
@Tobbe Tobbe merged commit 849e047 into redwoodjs:main Dec 21, 2021
@redwoodjs-bot redwoodjs-bot bot added this to the next-release milestone Dec 21, 2021
@Tobbe Tobbe deleted the tobbe-router-white-flash branch December 21, 2021 05:13
@thedavidprice thedavidprice modified the milestones: next-release, v0.41.0 Dec 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants