adobe / aem-guides-wknd-spa

MIT License
71 stars 97 forks source link

Components flashing on route change #7

Closed ifahrentholz closed 3 years ago

ifahrentholz commented 3 years ago

Hi, I experience an issue while routing. The Problem is that every component seems to be rerendered when navigating the page. Here is an GIF to demonstrate: route-flash-org

I'm using these Adobe AEM SPA node packages: "@adobe/aem-react-editable-components": "1.0.0", "@adobe/aem-spa-component-mapping": "1.0.0", "@adobe/aem-spa-page-model-manager": "1.0.0",

I could also reproduce this issue on the WKND tutorial. Is this a known issue or do you have a workaround to solve this problem ?

I'm guessing that this happens since every route loads it's own model.json (page configuration) and in this page configuration is everything included not only the content which changes but also "header", "footer" components which then get rerendered and cause the flashing.. Unfortunately I could not use the "shouldComponentUpdate" lifecycle hook since the components have to be extended from page or container provided by "@adobe/aem-react-editable-components" which seems to kill these hooks..

Kind regards IFahrentholz

godanny86 commented 3 years ago

Hi @ifahrentholz yes depending on how deep of the JSON hierarchy you load initially there will be a flicker with ajax request to get the page model that hasn't been loaded. I don't expect there to be a flicker after you have initially navigated to a page...

One approach would be to move the header and footer out the Page Template and into the App template. You will need to rewrite the navigation JS because only the client side will know which page are now "active" but I think it can work. You will still get a re-load in the main content region of the page but you can handle it easier.

ifahrentholz commented 3 years ago

Hi @godanny86, thanks for your answer. I've implemented a workaround which I think do what you suggested.

import React from "react";
import "./global/css/App.css";
import { Page, withModel } from "@adobe/aem-react-editable-components";
import CookieDisclaimerComponent from "./containers/CookieDisclaimer/CookieDisclaimerComponent";
import ComponentContainer from "./Layouts/ArticlePage/ComponentContainer";

class App extends Page {
  get headerProps() {
    const { cqChildren } = this.props;
    const firstCqChildrenKey = Object.keys(this.props.cqChildren)[0];
    const headerItem =
      cqChildren[firstCqChildrenKey][":items"].root[":items"].header;

    const headerProps = {
      cqItems: {
        root: {
          ...headerItem
        }
      },
      cqItemsOrder: ["root"]
    };

    return headerProps;
  }

  render() {
    return (
      <div>
        <ComponentContainer {...this.headerProps} />
        {this.childComponents}
        {this.childPages}
        <CookieDisclaimerComponent />
      </div>
    );
  }
}

export default withModel(App);

I'm not 100% sure if the way I extract the header data is recommendable. What's your opinion ?

import { Container } from "@adobe/aem-react-editable-components";
import React from "react";

const DEFAULT_TAG_NAME = "div";

class ComponentContainer extends Container {
  render() {
    if (!this.childComponents.length) {
      return null;
    }

    const TagName = this.props.tagName || DEFAULT_TAG_NAME;

    return (
      <TagName className={this.props.className}>{this.childComponents}</TagName>
    );
  }
}

export default ComponentContainer;