4

I tried to use import() instead of React.lazy to dynamically load components, but it didn't work.

App.js

import React, { useState } from 'react';
function App() {
  const [Com, setCom] = useState(null);

  const handleClick = () => {
      import("./A.js").then(c => {
        //console.log(c.default)
        setCom(c.default)
      }) 
  }

  return (
      <div>
        <button onClick={handleClick}>Load</button>
        { Com ? <Com /> : null }
      </div>
   );
}

export default App;

A.js

import React from "react";

export default function A () {
    return (<div>A</div>)
}

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

In fact, I printed out c.default. It's really a function.

c.default

ƒ A() {
  return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
    __source: {
      fileName: _jsxFileName,
      lineNumber: 4
    },
    __self: this
  }, "A");
}

2 Answers 2

4

Actually, it's not related to the dynamic import but the useState implementation. For example:

import Comp from "./A";

function App() {
  const [Com, setCom] = useState(Comp);

  return (
    <div>
      <Com />
    </div>
  );
}

Will throw the same error. That's because when you call useState(Comp), Comp "function" (which is the Component) been execute (you can test it by removing any <Com /> from the code and adding console.log inside A. The console.log still appears).

As a result Com is no longer a Component but a JSX element. When you try to render JSX element in a function way (meaning wrapping it with < and />, this error thrown.

The solution is either to set Com as a component (() => Com) or to render it as a JSX child ({Com})

import React, { useState } from "react";

function App() {
  const [Com, setCom] = useState(null);

  const handleClick = () => {
    import("./A.js").then(c => {
      //console.log(c.default)
      setCom(c.default);
    });
  };

  return (
    <div>
      <button onClick={handleClick}>Load</button>
      {/* <Comp /> */}
      {Com}
    </div>
  );
}

export default App;

https://codesandbox.io/s/answer-for-httpsstackoverflowcomq62125854863110-jj2wu

BTW, you can see the difference by console.dir the original outcome of import Comp from "./A"; and the outcome of useState(Comp)

import Comp from "./A";

function App() {
  const [Com, setCom] = useState(Comp);
  // console.log(1, Com, 2, Comp);
  console.dir(Com) // Object
  console.dir(Comp) // ƒ A()
  //...
}
Sign up to request clarification or add additional context in comments.

1 Comment

well actually I think react lazy is good. as I remember its only render when its need to and already give you back renderable compnent
3

If setState is called with a function, it will be executed with the previous value as the argument.

const [count,setCount] = useState(0)
setCount(prevCount => prevCount + 1)

In your case, rather than updating the state with the imported function, React will execute it and the result will be set as the state.

Solution:

setCom(() => c.default)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.