ionic-framework icon indicating copy to clipboard operation
ionic-framework copied to clipboard

bug: React fallback route causes flicker transition when routing between pages

Open ashishbairwa opened this issue 3 years ago • 11 comments

Prerequisites

Ionic Framework Version

  • [X] v4.x
  • [X] v5.x
  • [X] v6.x

Current Behavior

Summary:

Navigating to a random page right after implementing a default or a guarded route or a fallback route (following the method suggested in docs also https://ionicframework.com/docs/react/navigation#fallback-route) and then moving back and forth in the app leads to flickering/black glitch. Reloading the same page seems to correct this issue.

Context:

Hey there, We decided to go with ionic react for our patient application and we are nearly done with that. While setting page boundaries. I’ve decided to redirect the user based on a context value and prevent from falling into any unmatched route.

Here is how I declared that:

return <QueryClientProvider client={queryClient}>

    <IonApp>

      <IonReactRouter>

        <IonSplitPane contentId="main">

          {isLoggedIn && <Menu />}

          <IonRouterOutlet id="main">

            <Route path="/home" render={() => isLoggedIn ? <Home /> : <Redirect to="/login" />} exact={true} />

            <Route path="/payment-history" render={() => isLoggedIn ? <PaymentHistory /> : <Redirect to="/login" />} exact={true} />

            <Route path="/renewal" render={() => isLoggedIn ? <Packages /> : <Redirect to="/login" />} exact={true} />

            <Route path="/session-result/:type" render={() => isLoggedIn ? <SessionResult /> : <Redirect to="/login" />} exact={true} />

            <Route path="/treatment-plan" render={() => isLoggedIn ? <TreatmentPlan /> : <Redirect to="/login" />} exact={true} />

            <Route path="/profile" render={() => isLoggedIn ? <Profile /> : <Redirect to="/login" />} exact={true} />

            <Route path="/cancellation-policy" render={() => isLoggedIn ? <CancellationPolicy /> : <Redirect to="/login" />} exact={true} />

            <Route path="/terms-and-conditions" render={() => isLoggedIn ? <TermsAndConditions /> : <Redirect to="/login" />} exact={true} />

            <Route path="/help" render={() => isLoggedIn ? <Help /> : <Redirect to="/login" />} exact={true} />

            <Route path="/notifications" render={() => isLoggedIn ? <Notifications /> : <Redirect to="/login" />} exact={true} />

            <Route path="/getting-started" render={() => isLoggedIn ? <GettingStarted /> : <Redirect to="/login" />} exact={true} />

            <Route path="/progress-report" render={() => isLoggedIn ? <Progress /> : <Redirect to="/login" />} exact={true} />

            <Route path="/policies" render={() => isLoggedIn ? <Policies /> : <Redirect to="/login" />} exact={true} />

            <Route path="/login" render={() => !isLoggedIn ? <Authentication /> : <Redirect to="/home" />} exact={true} />

            <Route render={() => !isLoggedIn ? <Redirect to={'/login'} /> : <Redirect to={'/home'} />} />

            {/* <Redirect to={ !isLoggedIn ? '/login' : '/home' } /> */}

            {/* { MountedRoutes } */}

          </IonRouterOutlet>

        </IonSplitPane>

      </IonReactRouter>

    </IonApp>

    { process.env.REACT_APP_ENV_STAGE === 'dev' && <ReactQueryDevtools initialIsOpen={false} />}

  </QueryClientProvider>;

Navigation is working fine but there is an issue with how it’s animating, it ended up giving strange flickering while moving from one page to other page. One thing which I observed is when I refresh the page, flickering goes away and everything works as expected.

Here is the package.json:

{

  "name": "patient-app",

  "version": "0.0.2",

  "private": true,

  "dependencies": {

    "@capacitor/android": "3.3.4",

    "@capacitor/app": "1.0.7",

    "@capacitor/clipboard": "^1.0.6",

    "@capacitor/core": "3.3.3",

    "@capacitor/haptics": "1.1.3",

    "@capacitor/keyboard": "1.2.0",

    "@capacitor/splash-screen": "^1.2.0",

    "@capacitor/status-bar": "^1.0.6",

    "@dotmind/react-use-pwa": "^1.0.4",

    "@frogress/line": "^1.1.0",

    "@ionic/react": "^6.0.0",

    "@ionic/react-router": "^6.0.0",

    "@testing-library/jest-dom": "^5.11.9",

    "@testing-library/react": "^11.2.5",

    "@testing-library/user-event": "^12.6.3",

    "@types/jest": "^26.0.20",

    "@types/node": "^12.19.15",

    "@types/react": "^16.14.3",

    "@types/react-dom": "^16.9.10",

    "@types/react-router": "^5.1.11",

    "@types/react-router-dom": "^5.1.7",

    "chart.js": "^3.7.0",

    "classnames": "^2.3.1",

    "contentful": "^9.1.6",

    "date-fns": "^2.28.0",

    "firebase": "^9.6.1",

    "framer-motion": "^4.1.17",

    "html-react-parser": "^1.4.5",

    "ionicons": "^5.4.0",

    "lodash": "^4.17.21",

    "react": "^17.0.1",

    "react-chartjs-2": "^4.0.0",

    "react-dom": "^17.0.1",

    "react-icons": "^4.3.1",

    "react-lines-ellipsis": "^0.15.0",

    "react-onesignal": "^2.0.2",

    "react-otp-input": "^2.4.0",

    "react-phone-input-2": "^2.14.0",

    "react-query": "^3.34.12",

    "react-router": "^5.2.0",

    "react-router-dom": "^5.2.0",

    "react-scripts": "4.0.2",

    "styled-components": "^5.3.3",

    "typescript": "^4.1.3",

    "web-vitals": "^0.2.4",

    "workbox-background-sync": "^5.1.4",

    "workbox-broadcast-update": "^5.1.4",

    "workbox-cacheable-response": "^5.1.4",

    "workbox-core": "^5.1.4",

    "workbox-expiration": "^5.1.4",

    "workbox-google-analytics": "^5.1.4",

    "workbox-navigation-preload": "^5.1.4",

    "workbox-precaching": "^5.1.4",

    "workbox-range-requests": "^5.1.4",

    "workbox-routing": "^5.1.4",

    "workbox-strategies": "^5.1.4",

    "workbox-streams": "^5.1.4"

  }

I’m not sure what’s going wrong here. I tried all the other solutions mentioned on other posts as well but non of them working.

Expected Behavior

The default route should land on the target page and would not produce any flickering or black glitch while moving in the app

Steps to Reproduce

Please follow the git https://github.com/ashishbairwa/router-bug-ionic for the walkthrough.

Alternatively:

  1. Create a new ionic react app with template as sidemenu.
  2. Add two pages in the app
  3. Add the routes for them and place a default route following this https://ionicframework.com/docs/react/navigation#fallback-route
  4. Implement buttons to move back and forth across page.
  5. Moving to any page right after entering an undefined route in the URL would lead to glitch

Code Reproduction URL

https://github.com/ashishbairwa/router-bug-ionic

Ionic Info

Ionic:

Ionic CLI : 6.18.1 (C:\Users\Ashish Bairwa\AppData\Roaming\npm\node_modules@ionic\cli) Ionic Framework : @ionic/react 6.0.5

Capacitor:

Capacitor CLI : 3.3.3 @capacitor/android : 3.3.4 @capacitor/core : 3.3.3 @capacitor/ios : not installed

Utility:

cordova-res : not installed globally native-run : 1.5.0

System:

NodeJS : v16.13.1 (C:\Program Files\nodejs\node.exe) npm : 6.14.14 OS : Windows 10

Additional Information

Video proofs: https://youtu.be/kuEK9mkZx84 https://youtu.be/wg8vt6HvBlY

Linked issue in forum: https://forum.ionicframework.com/t/flickering-black-glitch-while-redirecting-to-some-other-page/220096/8

ashishbairwa avatar Feb 28 '22 16:02 ashishbairwa

Hello @ashishbairwa thanks for moving this conversation into Github from our discord conversation.

To help narrow the reproduction information, can you confirm if this accurately describes the problem as you understand it (based on your reproduction app)?

  1. Start at /home.
  2. Click "Move to my name" button to navigate to /ashish.
  3. Click the "Move to" button to navigate between pages.
  4. See that the transitions are smooth between pages .
  5. In the browser address bar, enter an invalid URL, such as: /abc (press enter/navigate).
  6. You are navigated back to /home.
  7. Click "Move to" button to navigate.
  8. Continue to click "Move to" buttons.
  9. See that the transitions are flickering/animating incorrectly.

sean-perkins avatar Feb 28 '22 18:02 sean-perkins

Hello @ashishbairwa thanks for moving this conversation into Github from our discord conversation.

To help narrow the reproduction information, can you confirm if this accurately describes the problem as you understand it (based on your reproduction app)?

  1. Start at /home.
  2. Click "Move to my name" button to navigate to /ashish.
  3. Click the "Move to" button to navigate between pages.
  4. See that the transitions are smooth between pages .
  5. In the browser address bar, enter an invalid URL, such as: /abc (press enter/navigate).
  6. You are navigated back to /home.
  7. Click "Move to" button to navigate.
  8. Continue to click "Move to" buttons.
  9. See that the transitions are flickering/animating incorrectly.

Yes, idea is to use the fallback route once. Right after that animation seems to break.

ashishbairwa avatar Feb 28 '22 18:02 ashishbairwa

Awesome, thanks! I've updated the title and see the same behavior on my end. We will capture this as a bug and prioritize in an upcoming sprint.

sean-perkins avatar Feb 28 '22 18:02 sean-perkins

Any progress on that?

ashish-yourphysio avatar May 23 '22 11:05 ashish-yourphysio

I have cloned and updated all the dependencies to check if it's resolved but i can still see flickering without even doing any thing. @sean-perkins

ashish-yourphysio avatar Sep 26 '22 08:09 ashish-yourphysio

It's been an year now. Do you guys have any update regarding this? @sean-perkins @liamdebeasi

ashish-yourphysio avatar Jan 16 '23 16:01 ashish-yourphysio

@ashish-yourphysio this is still an open issue in our backlog. The team has worked on many issues in the past year. Issues are prioritized by a variety of metrics. For community issues we weigh them on potential impact and upvotes (interest).

The code is open source and freely available to provide assistance that can lead to this issue being resolved sooner. Additional debugging insight into the problematic code or a fix to the issue would be appreciated by both the team and the community.

I understand this issue may be extremely critical to your application, but we are small team that tries to solve the largest sum problems across Ionic Framework (web components, Angular, React and Vue), Ionic docs (documentation and live examples) and the Stencil Framework Wrappers for DX for frameworks consuming our web components.

We have improvements for routing in design, but it is unclear if they would have an immediate impact to this specific issue, without identifying the root cause to the transition flickering with the fallback route defined.

sean-perkins avatar Jan 17 '23 00:01 sean-perkins

I can confirm that the problem still exists. If the page starts and there is a redirect that occurs than the transitions are flickering, here is a stackblitz - you can see in App.tsx that there are comments, regarding the redirects. Only "open" redirect causes flickering (btw only if you start the app from "/", if you refresh at "/customer/tabs/home" then everything works fine) The commented Redirects together gets the same result but no flickering occurs, even if you start the page from "/".

https://stackblitz.com/edit/aexcxb?file=src%2FApp.tsx (to try the page refreshes you can open the demo in full page) One last thing, the demo is not complicated so you'll see only drawback in performance but when there are more elements it's flickering in a bad way.

nadavhalfon avatar Dec 15 '23 18:12 nadavhalfon

I can confirm this is still existing in Ionic 7. Any news?

jemantilla avatar Apr 08 '24 18:04 jemantilla

Okay i think i was able to fix it. Previously this is how we use router guards:

<IonRouterOutlet animated>          
           {COMMON_ROUTES.map((item) => (
            <AuthenticatedRoute
              key={item.path}
              path={item.path}
              component={item.component}
              exact
            />
          ))}
</IonRouterOutlet>

Simply put, we use custom component and either return the component or return a redirect depending on the authentication. Turns out flickering only happens on navigation if IonRoute is not a direct child of the IonRouterOutlet

so what i did instead is something like:

<IonRoute
          path={LOGIN}
          render={(props) => unauthenticatedRender(props, baseUser, SignIn)}
          exact={true}
        />

where unauthenticatedRender:

import isNull from "lodash/isNull";
import { Redirect } from "react-router-dom";
import { LazyExoticComponent, Suspense } from "react";

import { BaseUser } from "../../../Common/models/user";
import { COMPLETE_SIGN_UP, DASHBOARD } from "../../constants/routes";

export const unauthenticatedRender = (
  props: any,
  user: BaseUser | undefined | null,
  C: (() => JSX.Element) | LazyExoticComponent<() => JSX.Element>
) => {
  const path = props.location.pathname;
  if (user !== undefined && !isNull(user)) {
    if (!user.completedSignUp) {
      return <Redirect to={COMPLETE_SIGN_UP} />;
    }
    return <Redirect to={DASHBOARD} />;
  } else {
    return (
      <Suspense>
        <C {...props} />
      </Suspense>
    );
  }
};

This solved it for me! hope this can help

jemantilla avatar May 01 '24 01:05 jemantilla

Okay i think i was able to fix it. Previously this is how we use router guards:


<IonRouterOutlet animated>          

           {COMMON_ROUTES.map((item) => (

            <AuthenticatedRoute

              key={item.path}

              path={item.path}

              component={item.component}

              exact

            />

          ))}

</IonRouterOutlet>

Simply put, we use custom component and either return the component or return a redirect depending on the authentication. Turns out flickering only happens on navigation if IonRoute is not a direct child of the IonRouterOutlet

so what i did instead is something like:


<IonRoute

          path={LOGIN}

          render={(props) => unauthenticatedRender(props, baseUser, SignIn)}

          exact={true}

        />

where unauthenticatedRender:


import isNull from "lodash/isNull";

import { Redirect } from "react-router-dom";

import { LazyExoticComponent, Suspense } from "react";



import { BaseUser } from "../../../Common/models/user";

import { COMPLETE_SIGN_UP, DASHBOARD } from "../../constants/routes";



export const unauthenticatedRender = (

  props: any,

  user: BaseUser | undefined | null,

  C: (() => JSX.Element) | LazyExoticComponent<() => JSX.Element>

) => {

  const path = props.location.pathname;

  if (user !== undefined && !isNull(user)) {

    if (!user.completedSignUp) {

      return <Redirect to={COMPLETE_SIGN_UP} />;

    }

    return <Redirect to={DASHBOARD} />;

  } else {

    return (

      <Suspense>

        <C {...props} />

      </Suspense>

    );

  }

};

This solved it for me! hope this can help

You are using "exact" which is not a fallback route and this is why you don't have the exact problem, the issue still persists:(

nadavhalfon avatar Jun 25 '24 22:06 nadavhalfon