Importing from index files in react - javascript

I cannot use index files for imports within my component library, but from the outside.
I'm using the following directory structure:
+ components
| index.js
+ atoms
| index.js
+ label
| label.jsx
| label.css
+ hoc
| index.js
+ withFrame
| withFrame.jsx
| withFrame.css
+ clientSpecificStyle
| index.js
| clientLabel.jsx
The index files are just exporting a default import
// just one sample
export { default as Label } from './label/label;
What I want to do is being able to distinguish between typical (basic) styling of components and client specific styling.
My clientLabel is just a label surrounded with a frame:
import React, {Component} from 'react';
// this doesn't work
import Label from '../atoms/index';
import withFrame from '../hoc/index';
// this works
import Label from '../atoms/label/label';
import withFrame from '../hoc/withFrame/withFrame';
#withFrame
class ClientLabel extends Component {
render() {
return (
<Label {...this.props}>{this.props.children}</Label>
);
}
}
export default ClientLabel;
When used from the "outside" (i. e. a demo page located on same folder hierarchy as components) using imports from the component index file, it works as expected. But I cannot import the HoC and the Label from the index files within the ClientLabel (fails with component/function undefined). It works however, when using the HoC and Label component files directly for import. The topmost index file (for the whole library) looks like this
export * from './atoms/index;
export * from './clientSpecificStyle/index';
//...
As I expect this project to grow into many separate components, it'd be more convenient to use index files for all imports, hence allow me to reorganize code as I see fit and only changing one line of code in the corresponding index file.
Is it possible to get this to work?
My webpack (v. 3.6) config works - apart from this issue - as expected. Just for clarity, here's the dev-config:
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: [
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/only-dev-server',
path.resolve('src', 'demo', 'demo.jsx')
],
output: {
path: path.resolve('dist'),
filename: 'bundle.js',
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'react-hot-loader/webpack!babel-loader',
exclude: [/node_modules/]
},
{
test: /\.css$/,
loaders: [
'style-loader?sourceMap',
'css-loader?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]'
]
}
]
},
devServer: {
contentBase: './dist',
hot: true
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve('src', 'demo', 'index.html'),
filename: 'index.html',
inject: 'body'
}),
new webpack.NamedModulesPlugin()
]
};

So just for completeness: the correct way to import is
import {Label} from '../atoms
as I've done a named export.
All kudos go to #ReiDien

Related

Webpack bundled library does not compile in React

I'm trying to bundle a standalone library with Webpack (v5) and use it in my React application.
The webpack.config.js file is pretty straightforward:
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: './TestWebpack.js',
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
output: {
filename: 'widgets.bundle.js',
path: path.join(__dirname, '..', 'ClientApp', 'src', 'dynamicBundles'),
library: {
name: 'widgets',
type: 'umd',
umdNamedDefine: true,
}
},
externals: {
'react': {
'commonjs': 'react',
'commonjs2': 'react',
'amd': 'react',
'umd': 'react'
},
'react-dom': {
'commonjs': 'react-dom',
'commonjs2': 'react-dom',
'amd': 'react-dom',
'umd': 'react-dom'
}
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: [
'#babel/preset-react'
],
}
}
},
]
},
};
The TestWebpack.js file is also very simple:
import React from 'react';
export default function TestWebpack(){
return (
<div>Test</div>
)
}
But when I compile it with Webpack and use it in my React application like this:
import React from 'react'
import { useEffect } from 'react'
import * as Widgets from '../dynamicBundles/widgets.bundle'
export default function TestUsingWebpack(){
useEffect(() => {
console.log('Object.keys(Widgets) - ', Object.keys(Widgets));
}, []);
return <div>Test</div>
}
I get the following errors:
Line 12: 'define' is not defined no-undef
Line 13: 'define' is not defined no-undef
Line 18: Unexpected use of 'self' no-restricted-globals
I can't figure out how to solve this. I see in the bundled file that in these lines an undefined variable 'define' is used. I saw in other answers that people put lines at the start to define it properly, but this bundle may change frequently so it's not an answer.
Maybe there's something wrong with my Webpack config file? I'm new to this and tried following the newest articles and Webpack documentation about standalone library but it's still not working.

webpack can't parse material ui button in a child component

I'm trying to bundle some React components with webpack.
But when I launch webpack --mode=development, i get this error :
Module parse failed: Unexpected token (6:4)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| const OwnButton = () => {
| return ( <Button color='secondary' variant='outlined'
This is my webpack config :
const path = require('path');
module.exports = {
entry: './src/app.js', // relative path
output: {
path: path.join(__dirname, 'public'), // absolute path
filename: 'js/bundle.js' // file name
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
}
};
This is my app.js config :
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Button from '#material-ui/core/Button';
import OwnButton from './Components/OwnButton';
const template = <Button>Hello from react jsx</Button>;
ReactDOM.render(template, document.getElementById('root'));
ReactDOM.render(<OwnButton/>, document.getElementById('React-OwnButton'));
And the wanted component (index.jsx) :
import Button from '#material-ui/core/Button';
const OwnButton = () => {
return (
<Button color='secondary' variant='outlined'>
Hello from my own component
</Button>
);
};
export default OwnButton;
And my .babelrc
{
"presets": ["#babel/preset-env", "#babel/preset-react"]
}
I don't understand how the call l.6 on app.js can work but not the on index.jsx l.6.
Where must I put the loader for this, in babel?
The problem is that you configured the Babel Loader to only intervene for .js files.
Try changing: test: /\.js$/ -> test: /\.((js)|(jsx))$/
This will make the loader work for .jsx files too.

React-hot-loader doesn't work with React-router-dom

So I've finally setup a working project with:
Electron (2.0.2)
React (16.4.0)
React-router-dom (4.2.2)
Webpack (4.11.0)
React-hot-loader (4.2.0)
And just when I started to develop some react components I noticed my project won't hot reload correctly. If I adjust something on the base url (/) it is updated correctly, but if I update something on a secondary url, say /test the webpack compiles, but I get the message Cannot GET /test.
I've tried a lot and I cannot seem to figure out what I am doing wrong. I looked into react-router-dom, since hot-reloading was an issue back in version 3.x, but they say it should be resolved now (in 4.x --> It works here..). Also i've added <base href="/"/> in my index.html so that is not it.
Can anyone tell me what I am doing wrong?
Webpack.common.js (This is merged into Webpack.dev.js)
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js'
},
resolve: {
modules: [path.resolve(__dirname), 'node_modules']
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
cacheDirectory: true,
presets: ['env', 'react'],
plugins: ['transform-runtime'],
env: {
development: {
plugins: ['react-hot-loader/babel']
},
production: {
presets: ['react-optimize']
}
}
}
}
]
}
};
Webpack.dev.js
module.exports = merge(common, {
mode: 'development',
devtool: 'eval-source-map',
entry: {
'app': [
'babel-polyfill',
'react-hot-loader/patch',
path.join(__dirname, 'src', 'index.js')
]
},
plugins: [
new webpack.HotModuleReplacementPlugin() // Enable hot module replacement
]
});
Index.js
import React from "react";
import ReactDOM from "react-dom";
import { AppContainer } from "react-hot-loader";
import { App } from "./app";
const render = Component => {
ReactDOM.render(
<AppContainer>
<Component/>
</AppContainer>,
document.getElementById("root")
);
};
render(App);
if (module.hot) {
module.hot.accept("./app", () => {
render(App);
});
}
App.js (my main entry point for my app, thus where I define my base routing)
import React, { Component } from 'react';
import { BrowserRouter, Route, NavLink } from 'react-router-dom';
import { Test } from './components/test';
import { Test2 } from './components/test2';
export class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<NavLink to="/">Home</NavLink>
<NavLink to="/test">Test</NavLink>
<div>
<Route exact path="/" component={Test}/>
<Route path="/test" component={Test2}/>
</div>
</div>
</BrowserRouter>
);
}
}
And the components 'test' and 'test2' are just plain simple react components with a 'hello world' text.
Anyone who sees anything that I am missing or doing wrong?
Thanks to this tutorial I found a way to adapt my project and get hot loading to work. It even made my code a bit cleaner and my build scripts simpeler.
Webpack.common.js
The first thing I needed to change was the babel-loader. I stole it from some tutorial, and it worked, but I did not know exactly what it did so I got rid of that code. I've also made the compilation of my code faster through the webpack.DllReferencePlugin.
Here is the updated webpack.common.js:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
module.exports = {
entry: {
app: [
'babel-polyfill',
'./src/index.js',
],
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
plugins: ['react-hot-loader/babel'],
cacheDirectory: true,
presets: ['env', 'react'],
},
}
],
},
plugins: [
new webpack.DllReferencePlugin({
context: path.join(__dirname),
manifest: require('../dist/vendor-manifest.json'),
}),
new HtmlWebpackPlugin({
title: '<my-app-name>',
filename: 'index.html',
template: './public/index.html',
}),
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, '../dist/*.dll.js'),
includeSourcemap: false // add this parameter
})
],
};
The AddAssetHtmlPlugin is required since the index.html is dynamically created (by the HtmlWebpackPlugin) for the dev server and you cannot hardcode the correct bundle import for the vendor.dll and the app.bundle (more here).
webpack.dev.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const webpack = require('webpack');
const path = require('path');
module.exports = merge(common, {
mode: 'development',
devtool: 'eval-source-map',
devServer: {
hot: true,
contentBase: path.resolve(__dirname, 'dist'),
historyApiFallback: true // Allow refreshing of the page
},
plugins: [
new webpack.HotModuleReplacementPlugin(), // Enable hot reloading
]
});
What did I change:
I moved the entry point up to webpack.common.
I Removed the 'react-hot-loader/patch' from the entry
(optional) I've added some config options for the webpack-dev-server.
Index.js
This is the file that caused the hot-reload to fail. Especially the if(module.hot) part caused it to fail. So I've changed it to the following:
import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { App } from './app';
const render = () => {
ReactDOM.render(
<AppContainer>
<App/>
</AppContainer>,
document.getElementById('app'),
);
};
render(App);
if (module.hot) {
module.hot.accept('./app', () => {
const NextApp = require('./app').default; // Get the updated code
render(NextApp);
});
}
The reason it works now is because now I fetch the new app and replace the old one, thus telling the hot-loader there has been a change. I could also just use module.hot.accept(), but that would make the react-hot-loader useless (you make use of the webpack hot-reloader) and this way I would also lose the state within my components every time I updated some code.
So there you go. I hope this will help anyone (other then myself).

React doesn't append class from className

I have a problem with React and don't know why, but React doesn't appent the attribute class in rendered HTML code. I import css file in body, import styles in react script, write className attribute, etc..
The result on page will be block without class – <div>test</div>
In App.js
import React, {Component} from 'react';
import styles from '../assets/main.css';
export default class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div className={styles.test}>test</div>
</div>
);
}
}
In main.css
.test {
color: red;
}
Webpack config
const HTMLWebpackPlugin = require('html-webpack-plugin');
const HTMLWebpackPluginConfig = new HTMLWebpackPlugin({
template: __dirname + '/app/index.html',
filename: 'index.html',
inject: 'body'
});
const webpack = require('webpack');
module.exports = {
entry: [
'react-hot-loader/patch',
__dirname + '/app/index.js'
],
devServer: {
hot: true
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.css$/,
loader: "style-loader!css-loader"
}
]
},
output: {
filename: 'transformed.js',
path: __dirname + '/build'
},
plugins: [
HTMLWebpackPluginConfig,
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
resolve: {
extensions: ['.js', '.jsx', '.css']
}
};
Importing your CSS stylesheet just causes Webpack to bundle it along with the rest of your JavaScript. You can't add styles.test as an attribute, that won't do anything. You need to specify the className as a string, just like you would for an HTML element.
render() {
return (
<div>
<div className="test" >test</div>
</div>
);
}
I think you should try importing your main.css file in your entry file i.e app/index.js. You don't need to add {styles.test} in className.
import './assets/main.css';
When the webpack bundle will run it will take all your css.
You just need to give same className like :
<div>
<div className='test'>test</div>
</div>
Also, make sure you have installed all loaders like file-loader, css-loader, url-loader, style-loader and included them in your webpack.config file.

React Component from jsx is not rendering to html

I am new to react and webpack configuration just trying with basic program.
I am not able to see any exception while running in localhost:8080 but not able to see the data in return statement of Render Component too. Please help me:
My React Component:
import React from 'react';
import ReactDOM from 'react-dom';
import SearchBar from '../search/search_bar';
export default class Index extends React.Component{
render()
{
return (<div>Hi Its me</div>);
}
}
html Page:
React Component is here:
<Index/>
<script src="bundle.js"></script>
webpack.config.js:
var path = require('path');
webpack = require('webpack');
var BUILD_DIR = path.resolve(__dirname, './src/client/public');
var APP_DIR = path.resolve(__dirname, './src/client/app');
module.exports = {
entry: APP_DIR + '/index.jsx',
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/public/'
},
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react']
}
}
]
},
resolve: {
extensions: ['', '.js', '.jsx'],
}
};
You can't render the component in an HTML file, so this:
<Index/>
<script src="bundle.js"></script>
is invalid. You need to render the application in your entry file index.js using the ReactDOM.render method, like this:
ReactDOM.render(
<Index />,
document.getElementById('#app'),
)
Then create that tag in your html file like this:
<div id="app"></div>
<script src="bundle.js"></script>
The principle of JSX is that it allows you to render HTML inside the JavaScript using a familiar syntax. All your components will always need to be rendered from inside your JS files.

Categories

Resources