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