Roy Ra
Roy Ra

Reputation: 604

How to solve Chunk Load Error in Create-React-app project?

I am deploying a project which was generated using CRA(Create-React-App), and all the babel and webpack configurations remain unmodified.

Also, I am deploying my React application using Static Bucket hosting feature of AWS S3.

The problem is, since there are no servers included, every time a new file is deployed, new chunk with random hashes are created.

When a client was in the website with old hash, and new hash was deployed, client gets Chunk Load Error.

To demonstrate this, build project with yarn build and go to /build folder to see chunks generated by webpack. Then using serve, I can demonstrate how my deployment will behave in my local machine.

After yarn serve was executed, go to appropriate localhost where build files are being served.

Then, at /build/static/js folder, rename the hashes of each chunk file as you wish.

Leave the browser alone, and restart yarn serve, which will serve renamed chunk files.

Then get back to the browser, and you will most likely get two errors:

Uncaught SyntaxError: Unexpected token '<'

Loading Chunk n failed.(missing: ~~)

The way I think can solve this problem is to HARD reload the webpage when ChunkLoadError occured. Sadly, window.reload(boolean) function has been deprecated, and doing only window.location.reload() doesn't solve this issue.

+) PS. I implemented Code-Splitting by using React.Lazy() in some codes.

Upvotes: 4

Views: 13774

Answers (1)

yolan PIBRAC
yolan PIBRAC

Reputation: 191

The solution you propose must work:

All you have to do is add these 2 meta tags to your index.html :

<meta http-equiv="Pragma" content="no-cache"/>
<meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate"/>

This will prevent the browser from using the cache when refresh your page

Second, use the ErrorBoundary component of react to put your refresh logic

Ex :

import React from "react";

const PageHasBeenForceRefreshed = "page-has-been-force-refreshed";

const retryPageLoading = () => {
  const pageHasAlreadyBeenForceRefreshed = JSON.parse(
    window.localStorage.getItem(PageHasBeenForceRefreshed) || "false"
  ) as boolean;

  if (!pageHasAlreadyBeenForceRefreshed) {
    window.localStorage.setItem(PageHasBeenForceRefreshed, "true");
    return window.location.reload();
  } else {
    window.localStorage.setItem(PageHasBeenForceRefreshed, "false");
  }
};

interface ErrorBoundaryProps {
  children: React.ReactNode;
}
export class ErrorBoundary extends React.Component<ErrorBoundaryProps, { hasError: boolean }> {
  constructor(props: Readonly<ErrorBoundaryProps>) {
    super(props);
    this.state = { hasError: false };
  }
  componentDidCatch(error: unknown, info: unknown) {
    retryPageLoading();
    this.setState({ hasError: true });
    console.log(error, info);
  }
  render() {
    if (this.state.hasError) {
      return <>Your error component</>;
    }
    return this.props.children;
  }
}

When one chunk will be missing in the index.html, the error boundary will catch the error and run the function "retryPageLoading()". The function "retryPageLoading()" use the local storage to avoid infinite loop. When an error occurred and the page has not refresh yes, the refresh is done by "window.location.reload()"

Finally, wrap your react app in the ErrorBoundary, in order to catch the chunk error when it is missing

import * as React from "react";
import { ErrorBoundary } from "./components/ErrorBoundary/ErrorBoundary";

const App: React.FunctionComponent = () => {
  return <ErrorBoundary>Your app</ErrorBoundary>;
};

Tell me if it is working. Good luck

Upvotes: 6

Related Questions