Can't import React component on server-side Next.js - javascript

I'm trying to import a very simple component from a library into a Next.js app.
I have stripped down my library to the bare minimum. It contains the following src/components/index.js:
import React from 'react'
const Container = ({ children }) => <div>{children}</div>
export { Container }
In my Next.js app, I created a pages/index.js that contains the following:
import React from 'react'
import { Container } from 'mylib'
class HomePage extends React.Component {
render() {
return <Container>Hello</Container>
}
}
export default HomePage
mylib is a local npm package which I have installed in my Next.js app using npm i ../mylib. It has a standard webpack config:
const path = require('path')
module.exports = {
entry: {
components: './src/components/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
libraryTarget: 'commonjs2'
},
mode: "production",
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
}
}
],
},
};
and then the package.json points to dist/components.js as the main file.
The following does work:
In my Next.js app, replace return <Container>Hello</Container> with return <div>Hello</div> (while keeping the import { Container } in place)
Refresh the page in the server (I'm running Next's dev server)
Restore the use of <Container> (the opposite of step 1)
Save the file --> hot-reload in the browser with the Container properly displayed (I added a className to be sure)
At this point, if I refresh the browser, I get the following error:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
which suggests I made a bad import/export, but I don't know where. And the error only occurs on the server-side.
What did I do wrong?

Container should be a default export, so this:
import React from 'react'
const Container = ({ children }) => <div>{children}</div>
export { Container }
should be this:
import React from 'react'
const Container = ({ children }) => <div>{children}</div>
export default Container
You could also one-line the Container component like this:
import React from 'react'
export default Container = ({ children }) => <div>{children}</div>

Related

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

how are you?
I have a React project with Webpack and Babel, and i'm trying to add Material UI (https://mui.com/) components, however, when i import a MUI component into my project i get the next error:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
A workaround i found is to add the ".default" import, but i don't understand why i'm not able to just import it the traditional way.
Here's a test component code that produces the error:
const React = require("react");
const Button = require("#mui/material/Button");
const Navbar = () => {
return <Button variant="contained">Contained</Button>;
};
module.exports = Navbar;
And here's my .babelrc and webpack.config code:
{
"presets": ["#babel/preset-env", "#babel/preset-react"]
}
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
output: {
path: path.join(__dirname, "/dist"),
filename: "index.bundle.js",
},
devServer: {
port: 8443,
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.scss$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
},
],
},
plugins: [
new HtmlWebpackPlugin({ template: "./src/index.html" }),
new MiniCssExtractPlugin(),
],
};
Does anyone know what i'm doing wrong? I know it might be silly, but i should be able to just import these components the "normal" way as stated in the MUI documentation, instead, i have to import them using the ".default" way.
Thanks in advance, sorry for my bad english.
MUI is designed to be used with EcmaScript modules (ESM) and not CommonJs (CJS). If you do want to use CJS, you can do that, but as ESM and CJS are not 100 % compatible, you will need some workarounds.
To be able to import with CJS syntax with
const Button = require("#mui/material/Button");
MUI would need to export (with CJS) with
module.exports = function Button(props) { ... }
But then the whole export of the Button module is the Button component, and the module could not export anything else.
So instead, everything is exported as named export, and the component itself is exported with the name default.
The ESM import understands the default and you can use the short form, but CJS just imports the javascript object, which is { default: ... }.
A
#mui/material/Button/index.js exports:
export { default } from './Button';
which can be imported with CJS with either:
const Button = require("#mui/material/Button").default;, or
const Button = require("#mui/material/Button/index").default;
B
#mui/material/index.js exports:
export { default as Button } from './Button';
export * from './Button';
which can be imported with CJS with:
const Button = require("#mui/material/index").Button;
( the export * is just "everything from Button", but there is nothing else exported from Button,
except e.g. getButtonUtilityClass from buttonClasses, but we are not interested in that)

SSR with data fetch without state management library

I am trying to make a SSR react app, but not able to pass props from express to the component.
What mistake am i doing?
server.js
import AppPage2 from './src/project02/LandingPage';
......
......
server.get('/project2',async (req,res)=>{
const context = {data:'test'}
const sheet = new ServerStyleSheet();
const content = ReactDOMServer.renderToString(sheet.collectStyles(
<StaticRouter location={req.url} context={context}>
<AppPage2 state={{"foo":"baar"}}/>
</StaticRouter>)
);
const styles = sheet.getStyleTags();
let html = appShell( 'Project 2', content,' project2.bundle.js',styles)
res.status(200).send(html);
res.end()
})
AppPage2(./src/project02/LandingPage)
import React from 'react';
import {Wrapper,Title} from './styleComponent/testStylePage'
.......
.......
class AppPage extends React.Component {
componentDidMount() {
console.log("{{API}}",this,this.props, this.props.staticContext)
}
render(){
return(
<Wrapper>
<Title>This is project 01 </Title>
</Wrapper>
)
}
}
export default AppPage;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import AppPage from './project02/LandingPage'
ReactDOM.hydrate(
<AppPage></AppPage>,
document.querySelector('#root')
);
webpack.client.conf
const path = require('path');
const glob = require("glob");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const entry = glob.sync("src/*.js")
.reduce((x, y) => Object.assign(x,
{
[y.replace('src/','').replace('.js','')]: `./${y}`,
}
), {});
module.exports = {
target: 'node',
mode: 'development',//development,production
entry: entry,
output: {
filename:'[name].bundle.js',
path: path.resolve(__dirname,'build/public/'),
publicPath: '/build/'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader'
},
},
]
},
plugins: [
// new BundleAnalyzerPlugin()
]
}
I am not able to log console.log("{{API}}",this,this.props, this.props.staticContext) from AppPage2(./src/project02/LandingPage) this page i am sending data from server.js(express)
Your props already passed to your component on Page2, but you're longing it using a function that will never been called,
componentDidMount() is invoked immediately after a component is mounted (inserted into the DOM tree).
in your case, you are not mounting any thing to the DOM, because react will only render your component to html and will not wait for your component to Mount, practically there is no DOM in your NodeJs server, and you're only rendering the component and return it as string, not inserting it to the DOM.
Your props are already there and you can console log them in your class constructor, and in your render method:
class AppPage extends React.Component {
constructor (props){
super(props)
console.log("{{API}}",this,this.props, this.props.staticContext)
}
render(){
//or console log it here
console.log("{{API}}",this,this.props, this.props.staticContext)
return(
<Wrapper>
<Title>This is project 01 </Title>
</Wrapper>
)
}
}
in this state your compoenent will mount and not did mount you can also console log your props using UNSAFE_componentWillMount but as it's name said it's unsafe
ReactJS.org
You can also create your own functions and it will works:
myFunction () {
console.log(this.props.state)
}
myFunction();

Webpack loader for .md file import for "react-markdown" npm library?

When i import a .md file , it gave me error, saying that it cannot read this particular .md file syntax,
I know there needs to be some kind of loader for it to parse the import, but when i looked online there was a loader called 'markdown-loader' which was only for marked npm package.
I am using react-markdown package to read md files
/* eslint-disable react/prefer-stateless-function */
import React, { Component } from 'react';
import ReactMarkdown from 'react-markdown';
import AppMarkdown from './posts/sample.md';
// import PropTypes from 'prop-types';
class CardDetails extends Component {
constructor() {
super();
this.state = { markdown: '' };
}
componentDidMount() {
// Get the contents from the Markdown file and put them in the React state, so we can reference it in render() below.
fetch(AppMarkdown)
.then(res => res.text())
.then(text => this.setState({ markdown: text }));
}
render() {
const { markdown } = this.state;
return <ReactMarkdown source={markdown} />;
}
}
CardDetails.propTypes = {};
export default CardDetails;
here's my markdown content sample.md
# React & Markdown App
- Benefits of using React... but...
- Write layout in Markdown!
i could not find package , i looked everywhere, do you know about the loader ?
Thankyou
Try to use raw-loader:
module.exports = {
module: {
rules: [
{
test: /\.md$/,
use: 'raw-loader'
}
]
}
}

Webpack bundle is not exported and can't be imported

I created my react Project A using Create-React-app. Then I bundle it them with Webpack and saved in my Git account.
Now I create another project(Called it Project B)in different directory. Download Project A directly from git. And trying to use it like so:
import React from 'react';
import ReactDOM from 'react-dom';
import { Main } from 'project-A/dist/main'
ReactDOM.render(<Main />, document.getElementById('root'));
I am getting an error like following:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
The webpack from Project A looks like this:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
module.exports = [
{
/*Client Side*/
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: {
loader: "html-loader",
options: { minimize: true }
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader,"css-loader"]
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./public/index.html",
filename:"./index.html"
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename:"[id].css"
})
]
}
]
I have research through the github and tried to change the name import, it still does not work.
Project A's component looks like this:
App.js:
render() {
return (
<div>
{this.renderCodeAuthCard()}
{this.renderConfirmCard()}
{this.renderVerifyCard()}
</div>
);
}
export default App;
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
Apparently webpack is not exporting the bundle file that is created in Project A. Since the import yields "undefine".
I am trying to find a way to export my webpack bundle file and use it in another project.
Any help will be appreciated
Its because you are not exporting any thing from index.js of Project A. The libraries installed by npm export functions from index.js.

Module parse failed, unexpected token with Webpack

I'm trying to build a React app using some simple methods of building views and components. When I run my webpack dev server I get the following error output:
Module parse failed: /Directory/To/router.js Unexpected token (26:2)
You may need an appropriate loader to handle this file type.
The line it complains about is when I first define my Router handler...
<Route handler={App}>
My full router.js is set as such:
// Load css first thing. It gets injected in the <head> in a <style> element by
// the Webpack style-loader.
import css from './src/styles/main.sass';
import React from 'react';
import { render } from 'react-dom';
// Assign React to Window so the Chrome React Dev Tools will work.
window.React = React;
import { Router } from 'react-router';
Route = Router.Route;
// Require route components.
import { App } from './containers/App';
import { Home } from './containers/Home';
import { StyleGuide } from './containers/StyleGuide';
import { Uploader } from './containers/Uploader';
const routes = (
<Route handler={App}>
<Route name="Home" handler={Home} path="/" />
<Route name="StyleGuide" handler={StyleGuide} path="/styleguide" />
<Route name="Uploader" handler={Uploader} path="/uploader" />
</Route>
)
Router.run(routes, function(Handler) {
return ReactDOM.render(<Handler/>, document.getElementById('app'));
});
In my .babelrc file I have my presets defined with react and es2015
My development webpack looks like this:
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: [
'webpack-dev-server/client?http://0.0.0.0:8080',
'webpack/hot/only-dev-server',
'./src/router'
],
devtool: 'eval',
debug: true,
output: {
path: path.join(__dirname, 'public'),
filename: 'bundle.js'
},
resolveLoader: {
modulesDirectories: ['node_modules']
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
resolve: {
extensions: ['', '.js']
},
module: {
loaders: [
// js
{
test: /\.js$/,
loaders: ['babel'],
include: path.join(__dirname, 'public')
},
// CSS
{
test: /\.sass$/,
include: path.join(__dirname, 'public'),
loader: 'style-loader!css-loader!sass-loader'
}
]
}
};
I've already tried to research this problem. I'm not finding any solutions to this specific instance. I'm curious to find why this is acting like this.
Edit 1
I managed to solve my problem by changing the directory name to my actual source directory and not in public/. But after correcting my mistake I stumbled upon two other errors dealing with my components, perhaps?
I now receive two errors in the browser:
Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components).
TypeError: _reactRouter.Router.run is not a function. (In '_reactRouter.Router.run', '_reactRouter.Router.run' is undefined)
I've found that this is commonly caused by not importing/exporting some things correctly. It's either coming from my router.js or my components that share their structure as below:
import React from 'react';
import { Link } from 'react-router';
const Uploader = React.createClass({
//displayName: 'Uploader',
render() {
return (
<div>
<h1>Uploader</h1>
</div>
)
}
});
export default Uploader;
You are exporting Uploader as default but importing it as a named export:
export default Uploader;
import { Uploader } from './containers/Uploader';
// instead do
import Uploader from './containers/Uploader';
Your Router configuration is a bit strange. What version of react-router are you using? Usually you can wrap your <Route> config into a <Router>. See the docs.

Categories