reactjsnext.jscode-splitting

Property *** does not exist on type 'ExoticComponent<any>


I try to do code split in React / Next.js project, but facing with obstacles.

I am following the howto here: https://reactjs.org/docs/code-splitting.html

Importing here:

const { SHA512 } = React.lazy(() => import("crypto-js"));

and using in useEffect here:

useEffect(() => {
    setTimeout(() => {
      let window2: any = window;
      if (window2.bp && process.env.NEXT_PUBLIC_BARION_ID) {
        const { totalPriceInt, unitPriceInt } = calcUnitAndTotalPrice(
          startPaymentIn,
          buyTicketData
        );
        window2.bp("track", "initiateCheckout", {
          contentType: "Product",
          currency: "HUF",
          id: startPaymentIn.eventId,
          name: buyTicketData?.name,
          quantity: startPaymentIn.quantity ?? 1.0,
          unit: "db",
          imageUrl: `https://ticket-t01.s3.eu-central-1.amazonaws.com/${buyTicketData?.imgId}_0.cover.jpg`,
          list: "ProductPage",
        });
        window2.bp("track", "setUserProperties", {
          userId: SHA512(getTempUserId(localStorage)).toString(), // <--- HERE
        });
      }
    }, 4000);
  }, []);

but get this error:

./components/LoginAndRegistration.tsx:25:9
Type error: Property 'SHA512' does not exist on type 'ExoticComponent<any> & { readonly _result: ComponentType<any>; }'.

  23 | import { GoogleLogin } from "react-google-login";
  24 | import axios from "axios";
> 25 | const { SHA512 } = React.lazy(() => import("crypto-js"));
     |         ^
  26 | 
  27 | interface LoginAndRegistrationProps {
  28 |   isRegistration: boolean;
error Command failed with exit code 1.

Do you know maybe why it is wrong?

Before it worked without lazy loading:

import { SHA512 } from "crypto-js";

I also tryed Next.js workaround:

import dynamic from "next/dynamic";
const { SHA512 } = dynamic(() => import("crypto-js"));

but got this error:

Type error: 'await' expressions are only allowed within async functions and at the top levels of modules.

  130 | 
  131 |   useEffect(() => {
> 132 |     const { SHA512 } = await import("crypto-js");
      |                        ^
  133 |     setTimeout(() => {
  134 |       let window2: any = window;
  135 |       if (window2.bp && process.env.NEXT_PUBLIC_BARION_ID) {

Solution

  • import("crypto-js") returns a promise, that once resolved, has the API of the module. It's not a React component, hence you can't use React.lazy() on it. (You could use React.lazy with a module that exports a component.)

    If you need to lazily load crypto-js within a React component, and then do things, you can do

    function MyComponent() {
      const [cryptoJs, setCryptoJs] = React.useState(null);
      React.useEffect(() => import("crypto-js").then(setCryptoJs), []);
      React.useEffect(() => {
        if(!cryptoJs) return;  // not loaded yet...
        const { SHA512 } = cryptoJs;
        // ...
      }, [cryptoJs]);
    }
    

    or

    function MyComponent() {
      React.useEffect(() => {
        import("crypto-js").then(({ SHA512 }) => {
          // ...
        });
      }, []);
    }
    

    However, depending on your bundler and bundler configuration it might be better to just use a regular import for the library, and instead lazily import (e.g. with React.lazy) the module that has this component that requires the library.