Skip to content
Advertisement

Error React createElement type is invalid expected a string for built-in components or a class function for composite components but got object

I am trying to change my client side rendered react app to be rendered on server side. I am using React with TypeScript. Here is the error:

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
/.../node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6984
    throw fatalError;

Here is my package.json dependencies:

  "devDependencies": {
    "@babel/core": "^7.19.3",
    "@babel/preset-env": "^7.19.4",
    "@babel/preset-typescript": "^7.18.6",
    "@babel/register": "^7.18.9",
    "@types/lodash": "^4.14.186",
    "@types/react-helmet": "^6.1.5",
    "babel-preset-react-app": "^10.0.1",
    "ignore-styles": "^5.0.1",
    "jest": "^27.5.1",
    "prettier": "2.7.1",
    "regenerator-runtime": "^0.13.9",
    "svg-inline-loader": "^0.8.2",
    "ts-loader": "^9.4.1",
    "typescript": "^4.8.4",
    "webpack-cli": "^4.10.0"
  }

And here is my rendered.js class where the error seems to be triggered:

import React from 'react'
import ReactDOMServer from 'react-dom/server'

// import our main App component
import App from '../../src/App';

const path = require("path");
const fs = require("fs");

export default (req, res, next) => {

    // point to the html file created by CRA's build tool
    const filePath = path.resolve(__dirname, '..', '..', 'build', 'index.html');

    fs.readFile(filePath, 'utf8', (err, htmlData) => {
        if (err) {
            console.error('err', err);
            return res.status(404).end()
        }

        // render the app as a string
        const html = ReactDOMServer.renderToString(<App />);

        // inject the rendered app into our html and send it
        return res.send(
            htmlData.replace(
                '<div id="root"></div>',
                `<div id="root">${html}</div>`
            )
        );
    });
}

And here is my index.tsx file:

import React from "react";
import ReactDOM from "react-dom";
import "./styles/index.css";
import {App} from "./App";
import reportWebVitals from "./reportWebVitals";
import { BrowserRouter } from "react-router-dom";
import * as _ from 'lodash'


// const root = ReactDOM.createRoot(
//   document.getElementById("root") as HTMLElement
// );
// root.hydrate(
//     <BrowserRouter>
//       <App />
//     </BrowserRouter>
// );

ReactDOM.hydrate(
    <BrowserRouter>
      <App />
    </BrowserRouter>,
    document.getElementById('root')
);

I’ve tried changing the imports of App.tsx although it didn’t work for me. Thanks in advance for your help!

Advertisement

Answer

Try to change the line in your rendered.js file

from

import App from '../../src/App';

to

import { default as App } from '../../src/App';

I think that’s because you use babel register or other transformer, transform default export into { default: [function App] }, so that the React.createElement function place the object but not the Component function into type field

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement