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

Migrate router and <app-view> to React #4525

Merged
merged 37 commits into from
Jan 20, 2020
Merged

Conversation

kravets-levko
Copy link
Collaborator

@kravets-levko kravets-levko commented Jan 7, 2020

What type of PR is this? (check all applicable)

  • Refactor

Description

  • Replace angular-router with universal-router + history
    • wrapper component for universal-router + history;
    • handle route.resolve map in the same manner as angular-router does;
    • handle <a> tags globally similarly to what angular does.
  • Convert <app-view> component to React
    • convert component to React;
    • update layout on route change;
    • alternative to route change events (history.block());
    • error handling;
    • mount new component instead of ng-app directive.
  • Update routes definitions to work with new router.
  • Cleanup
    • fix CSS (ones that are on custom tags of AngularJS components);
    • replace $route/$routeParams services;
    • replace $location service.
  • Testing
    • multi-org mode
    • check if jQuery is used somewhere on non-app pages (signup, login, etc.) and either (1) update code VanillaJS if it's easy or (2) restore jQuery (was removed in this PR) (Upd.: there is a very small piece of jQuery code in SaaS repo; except of that it's safe to remove jQuery)

Related Tickets & Documents

React migration #3071, closes #4159.

Mobile & Desktop Screenshots/Recordings (if there are UI changes)

No UI changes

@kravets-levko kravets-levko added Frontend Frontend: React Frontend codebase migration to React labels Jan 7, 2020
@kravets-levko kravets-levko self-assigned this Jan 7, 2020
@arikfr arikfr mentioned this pull request Jan 7, 2020
@arikfr
Copy link
Member

arikfr commented Jan 7, 2020

handle route.resolve map in the same manner as angular-router does;

I'm not sure we should keep this pattern. It's probably better to handle the load lifecycle in the page component itself.

handle tags globally similarly to what angular does.

Also not sure this worth implementing. We will need to do some extra work if we don't, but will end up with a "cleaner" and more explicit solution.

But we can start with both implemented (specially considering you already done the resolve) as a crutch to help with move the migration forward and once everything is working, make another PR that removes each of them and replaces it with better pattern.

@kravets-levko
Copy link
Collaborator Author

@arikfr Implementing both features is not that hard (as you mentioned, I already implemented route.resolve; handling tags globally is also another 15-20 LoC). So I'll start with that, and - as you mentioned - later we can discuss if we need that features and remove if necessary.

@kravets-levko kravets-levko force-pushed the migrate-router-to-react branch from 61fbc54 to b4ff41c Compare January 8, 2020 15:33
@kravets-levko kravets-levko force-pushed the migrate-router-to-react branch from a65f5f5 to 933b50a Compare January 10, 2020 19:03
@getredash getredash deleted a comment from srs231198 Jan 13, 2020
@kravets-levko kravets-levko force-pushed the migrate-router-to-react branch from e9a3311 to 6e8872c Compare January 14, 2020 13:46
@kravets-levko
Copy link
Collaborator Author

@gabrieldutra Fixed 👍 I also refined code a bit, so please review it as well. Thank you!

Comment on lines 415 to 421
render: currentRoute => (
<AuthenticatedPageWrapper key={currentRoute.key}>
<ErrorBoundaryContext.Consumer>
{({ handleError }) => <DashboardPage {...routeParams} onError={handleError} />}
{({ handleError }) => <DashboardPage {...currentRoute.routeParams} onError={handleError} />}
</ErrorBoundaryContext.Consumer>
</AuthenticatedPageWrapper>
),
Copy link
Member

Choose a reason for hiding this comment

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

Not sure if it's that worth it at this point, but it looks like pages follow the same pattern when being defined, if this is not intended to be changed (and every page should follow this notation), we perhaps could abstract it a bit more with a closure returning this function? (seems we can tweak it to receive only the page component and some extra routeParams):

{
  path: "/dashboard/:dashboardSlug",
  render: authenticatedPage(DashboardPage, { extraData: "dashboard-page" }),
}

BTW, if I had to choose, I think it's better to pass routeParams as separate props (like you did to List Pages) and not directly to the props (I don't know, but it feels risky to me to leave props that open 😬).

Well, lmk your opinion

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

  1. Yes, indeed it makes sense. But let's do it in a follow-up PR - I don't want to add anything more to this one (except of bug fixes related to PR's subject);
  2. With new router it's safe to spread route params - that object contains only values from URL (in this example - dashboardSlug) and resolved values (which will be removed soon), Router does not add anything to it (unlike Angular's router which was merging params from URL, resolved values and query string params).


useEffect(() => {
if (bodyClass) {
document.body.classList.toggle(bodyClass, true);
Copy link
Member

Choose a reason for hiding this comment

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

(Not blocking; just to understand if we can improve this further on)

Do we really need to set this class on the body element instead of one of the children that we can control w/ React?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ATM - yes, because it stretches few wrapper components when that class is set. To avoid this, we have to revisit some layout styles which I didn't want to do in this PR.

path: "/alerts/:alertId([0-9]+)/edit",
title: "Alert",
render: currentRoute => (
<AuthenticatedPageWrapper key={currentRoute.key}>
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need to duplicate this piece of logic (AuthenticatedPage + ErrorBoundry) on every page?

Copy link
Member

Choose a reason for hiding this comment

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

Also, passing handleError to every page feels like too much. Can't we solve this with some global state/context?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As I mentioned in reply to @gabrieldutra, I'm going to revisit this and few other things in a follow-up PR; currently it's stable, and I prefer not to add any more changes to this PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Also, passing handleError to every page feels like too much. Can't we solve this with some global state/context?

Yes, and that's what I plan to do. There is a context already though; it's easy to use it with function-based components and a bit trickier with class-based ones - I didn't want to deal with it now so just passed it as a prop everywhere.

querySnippetsRoutes,
organizationSettingsRoutes,
usersListRoutes,
userProfileRoutes,
Copy link
Member

Choose a reason for hiding this comment

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

👍 good find.

Not something to spend on time now, but it's interesting to see how Next.js handles this. Because they have dynamic imports and produce optimized builds.

client/app/config/index.js Show resolved Hide resolved
Copy link
Member

@arikfr arikfr left a comment

Choose a reason for hiding this comment

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

One thing I noticed which is broken:

  • Start a new query.
  • Execute.
  • Click on New Visualization.

Unlike before, it's not saved and you can't save the viz.

@kravets-levko
Copy link
Collaborator Author

@arikfr Seems it was broken during Query pages migration (I reproduced it on master). I'll open separate PR for it as well, let's not block this one with this bug.

@arikfr
Copy link
Member

arikfr commented Jan 20, 2020

Here's another one:

  1. Open homepage.
  2. Click on the "Now Param" dashboard.

You get every parameter update added to the history so going back feels like it's not working (you need to click lots of time until you get back to the homepage).

@kravets-levko kravets-levko force-pushed the migrate-router-to-react branch 2 times, most recently from 62e2cc6 to 9d00be5 Compare January 20, 2020 16:58
@kravets-levko kravets-levko force-pushed the migrate-router-to-react branch from 9d00be5 to f8e7334 Compare January 20, 2020 17:11
@arikfr
Copy link
Member

arikfr commented Jan 20, 2020

I triggered another build.

@kravets-levko
Copy link
Collaborator Author

@arikfr That test was still not stable even after my previous fix; new fix worked fine on my PC, need to try on CI now.

A short explanation about history bloating bug fix: the actual issue is in useDashboard hook and it's not fixed here, because it's not related to a router itself and also requires it's own proper fix and discussion (PR is coming soon). In short: every time dashboard it updated, useDashboard decides to update URL as well; when refreshing dashboard - each widget updates dashboard twice - therefore so much history entries. I'm not sure how it worked with $location service (probably it updates URL only if something really changes). But since that updates are not necessary - they should not happen at all.

Also, while reproducing this bug on master, I noticed that when changing param values manually (or update other query string params), and then hitting Back button in browser - URL gets changed, but page does not update. It happens on dashboard page, all list pages and on query pages. So I decided to just replace URL (not push new history entry) - it (a sort of) fixed both bugs: history is not flooded with entries that pages cannot handle anyway.

@kravets-levko kravets-levko force-pushed the migrate-router-to-react branch from ff4e311 to 7e3f9cf Compare January 20, 2020 18:29
@kravets-levko kravets-levko merged commit a682265 into master Jan 20, 2020
@kravets-levko kravets-levko deleted the migrate-router-to-react branch January 20, 2020 18:56
@arikfr
Copy link
Member

arikfr commented Jan 20, 2020

🙌

@jezdez
Copy link
Member

jezdez commented Jan 22, 2020

Congrats!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Frontend: React Frontend codebase migration to React Frontend
Projects
None yet
Development

Successfully merging this pull request may close these issues.

React Migration: Routing library
4 participants