2

I have pages or large components that need to be rendered after the main page loads. I have them lazily loaded but am getting an error when I use in createElement():

LazyExoticComponent | LazyExoticComponent is not assignable to parameter of type 'string | FunctionComponent<RefAttributes> | ComponentClass<RefAttributes, any>'

What I've tested:

import Page2 from "./Page2";
import Page3 from "./Page3";
const pages = [Page2, Page3];

//in any method in the react component
let s = [Page2];
let ss = [Page2, Page3];
let sss = pages[0];
let r = React.createElement(s[0]); //this works
let rr = React.createElement(ss[0]); //does not work
let rrr = React.createElement(sss); //does not work

Using latest versions of Typescript, React, React-Scripts does not seem to have any affect.

Typescript ^3.9.9, React ^16.14.0, React-Scripts ^2.1.8, "@types/react": "^17.0.0", "react-dom": "^16.13.1", no @types/react-dom

  • why use createElement now when you can use JSX currently? – Shubham Khatri Apr 04 '21 at 17:09
  • The example is shortened to show the big picture of what's happening with the step variable. In my actual program, the step variable is stored in state before being added to the return of render(). I'm unsure if there are benefits, but that is how I got the project from a previous group. –  Apr 04 '21 at 17:17
  • Error is: LazyExoticComponent | LazyExoticComponent | ... is not assignable to paramter of type 'string'. Editted post to more accurately describe situation. –  Apr 05 '21 at 05:59
  • 1
    I understand how and why your post below works +1. I tried for an hour but couldn't replicate in sandbox. Updated post to hopefully cut out the unnecessary parts. –  Apr 05 '21 at 07:30
  • Seems like it. I have no idea why. –  Apr 05 '21 at 07:36

2 Answers2

1

This particular problem has to do with the generics of the React.Component type. If using an array, each element page's React.Component 'signature' has to be the same or it will throw this error. For example:

export default class Page2 extends React.Component {...}
export default class Page3 extends React.Component {...}

//in main file
const Page2 = lazy( ()=>import('./Page2') );
const Page3 = lazy( ()=>import('./Page3') );
const pages = [Page2, Page3];
...
let page = React.createElement(pages[1]);

This will work because the 'signatures' of Page2 and Page3 are the same. Namely, they are <>.

export default class Page2 extends React.Component<IPage2Props, IPage2State>
export default class Page3 extends React.Component<IPage3Props, IPage3State>

These on the other hand, will not work and are what caused the issue I posted about. Even if IPage3Props and IPage2Props have the same contents in the interface/type, it TS will still not understand.

This issue, as far as I can tell, does not have anything to do with the environment or the versions of TS, or React used.

The obvious solution is to make the 'signatures' the same. This may not be practical and I am unsure what else can be done to fix the issue.

desertnaut
  • 57,590
  • 26
  • 140
  • 166
  • If you lost the email address of your original account, you can flag one of your posts for moderator attention, or click the "Contact us" button at the bottom of the page. In either case, explain the issue clearly so that your accounts can be merged. – cigien Apr 05 '21 at 19:23
  • Yes. It is related to Typings. Lazy and Suspense would work as normal. – Ajeet Shah Apr 06 '21 at 04:21
-1

You can simply use lazy and Suspense with a fallback content.

The lazy component should then be rendered inside a Suspense component, which allows us to show some fallback content (such as a loading indicator) while we’re waiting for the lazy component to load.

An example:

import React, { Component, createElement, lazy, Suspense } from "react";

const Page2 = lazy(() => import("./Page2"));
const Page3 = lazy(() => import("./Page3"));

const pages = [Page2, Page3];

class App extends Component {

  render() {
    let s = [Page2];
    let ss = [Page2, Page3];
    let sss = pages[0];
    let r = createElement(s[0]);
    let rr = createElement(ss[0]);
    let rrr = createElement(sss);
    return (
      <>
        <h2>App</h2>
        <Suspense fallback="loading..."> {/* <== using the Suspense /*}
          {r} {rr} {rrr}
        </Suspense>
      </>
    );
  }
}

Here is CodeSandbox

Ajeet Shah
  • 18,551
  • 8
  • 57
  • 87