React static site with webpack - javascript

I'm trying to create a static site following this tutorial http://jxnblk.com/writing/posts/static-site-generation-with-react-and-webpack/
I'm currently getting this error: ERROR in ReferenceError: document is not defined
This is my app.jsx file:
import React from 'react';
import ReactDOM from 'react-dom';
import { Router } from 'react-router';
import routes from './config/routes';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<Router>{routes}</Router>
);
}
}
export default App;
module.exports = function render(locals, callback) {
var html = React.renderToStaticMarkup(React.createElement(App, locals))
callback(null, '<!DOCTYPE html>' + html)
}
Routes:
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import Layout from '../components/layout';
import Home from '../components/home';
export default (
<Route path="/" component={Layout}>
<IndexRoute component={Home} />
</Route>
);
layout.jsx component:
import React from 'react';
import Nav from './nav';
import Footer from './footer';
import Styles from '../styles.scss';
class Layout extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<div id="layout" className={Styles.main}>
<Nav />
{this.props.children}
<Footer />
</div>
)
}
}
Layout.PropTypes = {
children: React.PropTypes.object.isRequired
}
export default Layout;
and my webpack.config.js file:
/* eslint-disable */
var path = require('path'),
webpack = require('webpack'),
autoprefixer = require('autoprefixer'),
OpenBrowserPlugin = require('open-browser-webpack-plugin');
StaticSiteGeneratorPlugin = require('static-site-generator-webpack-plugin');
data = require('./data');
var configServe = {
port: 9100,
};
module.exports = {
devServer: {
hot: true,
inline: true,
historyApiFallback: true,
progress: true,
port: configServe.port,
},
entry: [
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:' + configServe.port,
path.resolve(__dirname, './src/app.jsx'),
],
output: {
path: __dirname,
filename: './dist/bundle.js',
libraryTarget: 'umd'
},
module: {
loaders: [
{
// JSX files :
test: /\.js[x]?$/,
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/,
loader: 'babel-loader?presets[]=es2015&presets[]=react',
},
{
// CSS files :
test: /\.css?$/,
include: path.resolve(__dirname, 'src'),
loader: 'style-loader!css-loader!postcss-loader',
},
{
// SCSS files :
test: /\.scss?$/,
include: path.resolve(__dirname, 'src'),
loader: 'style-loader!css-loader!postcss-loader!sass',
},
{
test: /\.(png|jpg)$/,
include: path.resolve(__dirname, 'src'),
loader: 'file-loader'
},
{
test: /\.svg$/,
loader: 'svg-inline'
}
],
},
postcss: [
autoprefixer({ browsers: ['last 3 versions'] }),
],
plugins: [
// Avoid publishing files when compilation fails
new StaticSiteGeneratorPlugin('./dist/bundle.js', data.routes, data),
new webpack.NoErrorsPlugin(),
new webpack.HotModuleReplacementPlugin(),
new OpenBrowserPlugin({ url: 'http://localhost:' + configServe.port }),
],
resolve: {
extensions: ['', '.js', '.jsx'],
},
stats: {
// Nice colored output
colors: true,
},
// Create Sourcemaps for the bundle
devtool: 'source-map',
};
Hopefully someone can help me figure this out. I'd like to eventually bundle this all in a non-opinionated way of creating static webpages with React and posting on github.

I guess this line is causing error:
var html = React.renderToStaticMarkup(React.createElement(Root, locals))
There you are using some Root component, but concluding from your code it is not defined. Also it is defined in tutorial (you have pointed out) by this line: var Root = require('./components/Root.jsx')

Related

HMR not triggering sometimes for components

This has been baffling me for a few hours now & I can't seem to work out why. My project structure is below. Sometimes a change in views/Dashboard/index.jsx will trigger a hot reload & other times it will not. It's weird but when it works, after about an hour or so, it will just suddenly stop working...
index.js
import 'react-hot-loader'
import 'bootstrap/dist/css/bootstrap.min.css'
import React from 'react'
import ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import { library } from '#fortawesome/fontawesome-svg-core'
import { faChartBar, faThList, faCogs, faClipboard, faFilter, faNotEqual } from "#fortawesome/free-solid-svg-icons"
import configureStore, { history } from './redux/store'
import App from './App'
library.add(faChartBar, faThList, faCogs, faClipboard, faFilter, faNotEqual)
let store
function render(Component) {
ReactDOM.render(
<AppContainer>
<Component store={store} />
</AppContainer>,
document.getElementById('root')
)
}
configureStore()
.then(_store => {
store = _store
render(App)
if (module.hot) {
// Reload components
module.hot.accept('./App', () => {
render(App)
})
}
})
.catch(err => {
console.log(err)
})
App.jsx
import { hot } from 'react-hot-loader/root'
import React from 'react'
import { Provider } from 'react-redux'
import { BrowserRouter as Router } from 'react-router-dom'
import PropTypes from 'prop-types'
// Routes
import routes from './routes'
const App = ({ store }) => {
return (
<Provider store={store}>
<Router>{routes}</Router>
</Provider>
)
}
App.propTypes = {
history: PropTypes.object,
}
export default hot(module)(App)
routes/index.js
import React from 'react'
import { Route, Switch } from 'react-router'
// Components
import Dashboard from '../views/Dashboard'
// Views
import NoMatch from '../views/NoMatch'
export default (
<Switch>
<Route path="/" component={Dashboard} exact={true} />
<Route component={NoMatch} />
</Switch>
)
views/Dashboard/index.jsx
import React, { Component } from 'react'
export default class Dashboard extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
Hello World
</div>
)
}
}
If I make any change to the first 4 files listed (index.js, App.jsx, routes/index.js, containers/Dashboard.js) then the hot reloading works & the page is reloaded. However, if I attempt to make a change to views/Dashboard/index.jsx, for example, change "Hello World" to "Foo Bar" then the hot reloading is not honoured.
However I have now taken out the intermediary step of redux & changed the routers/index.js to use the direct component from views/Dashboard/index.jsx & it still does not reload.
How can this be? If I set the component on the Route to the container listed above, it hot reloads for that file but not this JSX one? Is this to do with the structure inside my main component views/Dashboard/index.jsx? If so, what? As it looks fine to me.
EDIT
My complete webpack config is:
webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const DotenvPlugin = require('dotenv-webpack')
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: 'js/[name].bundle.js',
},
resolve: {
extensions: ['.js', '.jsx'],
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'babel-loader', // Using Babel to compile JS files
},
{
test: /\.(css|scss|sass)$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
test: /\.(png|svg|jpg|gif|woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1000000,
name: 'img/[hash].[ext]',
},
},
],
},
{
test: /\.html$/,
loader: 'html-loader'
}
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public', 'index.html'),
filename: './index.html',
}),
new DotenvPlugin({
systemvars: true,
}),
],
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
}
webpack.dev.js
const webpack = require('webpack')
const merge = require('webpack-merge')
const path = require('path')
const common = require('./webpack.common.js')
module.exports = merge(common, {
mode: 'development',
entry: path.resolve(__dirname, 'src', 'index.js'),
// https://github.com/gaearon/react-hot-loader#source-maps
devtool: 'eval',
devServer: {
contentBase: path.join(__dirname, 'dist'),
open: true,
compress: true,
hot: true,
historyApiFallback: true
},
resolve: {
alias: {
'react-dom': '#hot-loader/react-dom'
}
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
],
optimization: {
usedExports: true,
},
})
.babelrc
{
"presets": [
[
"#babel/preset-env",
{
"useBuiltIns": "entry",
"corejs": "3.0.0",
"targets": {
"esmodules": true
}
}
],
"#babel/preset-react"
],
"plugins": [
"react-hot-loader/babel"
]
}
I have updated the files listed to show what I currently have. This is a very weird error as sometimes the hot reloading will work & other times it will not. Hot reloading always works for files; index.js, App.jsx, & routes/index.jsx but sometimes it will work for views/Dashboard/index.jsx & sometimes it will not.
I was previously following the Getting Started guide but this thread helped me realise this was out of date. I have now just followed the README as is mentioned. I have also attempted to take snippets from this article as well on setting up HMR with Webpack 4.
Versions
webpack: 4.41.6
webpack-dev-server: 3.10.3
babel-loader: 8.0.6
#hot-loader/react-dom: 16.11.0
react: 16.12.0
react-hot-loader: 4.12.19
I have the below response in the console so HMR is clearly enabled
I am trying to replicate exactly what is in the examples as recommended

Cannot import react js component

I trying to import a react component to jsx file but it throws this exception:
This is my main code:
import React, { Component } from "react";
import Sidebar from "./Sidebar";
class App extends Component{
render(){
return (
<Sidebar/>
);
}
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
and this is my Sidebar component:
import React, { Component } from "react";
export default class Sidebar extends Component{
render(){
return (
<h1>Hello Sidebar</h1>
);
}
}
My folders structure:
I post simpler version which I know does work:
./index.js :
import React from 'react';
import ReactDOM from 'react-dom';
import Application from './components/Application'
ReactDOM.render(<Application />, document.getElementById('root'));
./components/Application :
import React from 'react';
class Application extends React.Component {
render() {
return (
<div className="App">
My Application!
</div>
);
}
}
export default Application;
This should be everything needed to make it work.
If you want to shorten the above even more, by removing the export line at the bottom, a less traditional approach would be defining the class like this...
export default class Application extends React.Component {...}
Looks like you haven't added rule for .jsx file in webpack.config.js.
Since you have both .js and .jsx files you need to tell webpack to load files with extension .js and .jsx. Add below config to webpack.config.js in rules section
{
//tell webpack to use jsx-loader for all *.jsx files
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: "babel-loader"
}
And add extensions like
resolve: {
modules: [
path.resolve("./src"),
path.resolve("./node_modules")
],
extensions: [".js", ".jsx"]
}
Below is the working webpack.config.js file for your ref
module.exports = {
target: "web",
entry: [
"whatwg-fetch",
'webpack-dev-server/client',
'webpack/hot/only-dev-server',
'babel-polyfill',
"./src/index.js"
],
output: {
path: __dirname + 'build',
publicPath: '/',
filename: "bundle.js"
},
plugins: [new HtmlWebpackPlugin({
template: "index.html"
}),
new CompressionPlugin({
asset: "[path].gz[query]",
algorithm: "gzip",
test: /\.js$|\.jsx$|\.css$|\.html$/,
threshold: 10240,
minRatio: 0.8
}),
new webpack.HotModuleReplacementPlugin(),
// enable HMR globally
new webpack.NoEmitOnErrorsPlugin()
],
module: {
rules: [
{
//tell webpack to use jsx-loader for all *.jsx files
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: "babel-loader"
},
{
test: /\.css$/,
loader: "style-loader!css-loader"
},
{
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/,
use: [
{
loader: 'file-loader',
options: {}
}
]
},
{
test: /\.(eot|ttf)$/,
loader: "file-loader",
},
{
test: /\.scss$/,
loaders: ["style-loader", "css-loader", "sass-loader"]
}
]
},
resolve: {
modules: [
path.resolve("./src"),
path.resolve("./node_modules")
],
extensions: [".js", ".jsx"]
},
devServer: {
watchOptions: {
// Needed for Windows Subsystem for Linux dev environment:
poll: true
},
contentBase: './build'
},
devtool: "cheap-module-eval-source-map",
node: {
child_process : "empty",
fs: "empty"
}
};

React Route not displaying component on production

I am developing a React Webapp and I am using webpack for automating some tasks. The development mode works fine, I can see all components showing up, but when I open index.html from /dist folder where I have bundle.js and bundle.css same directory the webapp does not show the components.
I tried to upload on a apache web server whole /dist folder and tried to access like localhost/dist still I cannot see the comopnents.
Any idea how to make it so the files within dist folder I can use, in fact that's why I am using webpack to automate tasks so I can use this folder for production.
Webpack:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
const path = require('path');
module.exports = (env, argv) => {
console.log("ENV DETECTED: " + argv.mode);
return {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
//publicPath: '/', // Remove this when running in production
},
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,
// 'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
minimize: true
}
},
{
loader: 'postcss-loader',
options: {
config: {
path: './postcss.config.js'
}
}
}
]
},
{
test: /\.scss$/,
use: [
argv.mode !== 'production' ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1,
minimize: true
}
},
{
loader: 'postcss-loader',
options: {
config: {
path: './postcss.config.js'
}
}
},
"sass-loader"
]
}
],
},
devServer: {
historyApiFallback: true,
},
plugins: [
new CleanWebpackPlugin('dist', {}),
new HtmlWebPackPlugin({
template: "src/index.html",
filename: "./index.html"
}),
new MiniCssExtractPlugin({
filename: "bundle.css",
chunkFilename: "bundle.css"
}),
require('autoprefixer'),
]
}
};
Routes:
import React from "react";
import { Switch, Route } from 'react-router-dom';
import Helloworld from './components/helloworld/helloworld.component';
import SecondView from './components/secondview/secondview.component';
import ThirdView from "./components/thirdview/thirdview.component";
const AppRoutes = () => (
<main>
<Switch>
<Route exact path='/' component={Helloworld}/>
<Route path='/secondview' component={SecondView}/>
<Route path='/thirdview' component={ThirdView}/>
<Route path='/thirdview/:number' component={ThirdView}/>
</Switch>
</main>
);
export default AppRoutes;
Helloworld component:
import React from 'react';
import HelloworldApi from './helloworld.api';
import { Link , withRouter } from 'react-router-dom';
require('./helloworld.style.scss');
class Helloworld extends React.Component {
constructor(props) {
super(props);
this.state = {
persons: [],
};
}
componentDidMount() {
console.log("I am being called helloworld component...");
HelloworldApi.getAll().then(response => {
if (response.data) {
console.log(response.data);
const persons = response.data;
this.setState({persons});
} else {
console.log(response);
}
});
}
render() {
return (
<div className={"boxDiv"}>
<p>Helloworld components</p>
<h1>Hello World</h1>
<h3>Web pack and React aree awesome together!!! test</h3>
{this.state.persons.map(person => <li>{person.name}</li>)}
<Link to='/secondview'>go to secondview</Link>
</div>
);
}
}
export default withRouter(Helloworld);

React-hot-loader not re-rending components

I have an application that is serving an EJS view with my React.js bundle loaded in a script tag.
I have followed the instructions for react-hot-loader v3 and #next, neither are working. The are no errors in the console and the components are not being re-rendered on change.
Here is my app entry point:
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import injectTapEventPlugin from 'react-tap-event-plugin';
import { AppContainer } from 'react-hot-loader'
import { store } from './routes';
import App from './containers/App';
const render = Component => {
ReactDOM.render(
<Provider store={store}>
<MuiThemeProvider>
<AppContainer>
<Component />
</AppContainer>
</MuiThemeProvider>
</Provider>,
document.getElementById('root'))
};
render(App);
//registerServiceWorker();
// Webpack Hot Module Replacement API
if (module.hot) {
console.log('module.hot exists');
module.hot.accept('./containers/App', () => {
console.log('Reloading app');
render(App);
})
}
Here is the App files that the entry point is calling:
import React from 'react';
import Navigation from '../components/Navigation';
import { hot } from 'react-hot-loader'
import createRoutes from '../routes';
const routes = createRoutes();
const App = () => {
return ([
<Navigation key="app-navigation" />,
<main style={{ height: 'calc(100% - 85px)' }} key="app-main">{routes}</main>
])
};
export default hot(module)(App)
Here is my webpack dev config:
const devBrowserRender = {
devtool: 'source-map',
context: PATHS.app,
entry: {
app: ['babel-polyfill', 'react-hot-loader/patch',
'./index']
},
node,
devServer: {
contentBase: PATHS.assets,
hot: true
},
output: {
path: PATHS.assets,
filename: '[name].dev.js',
publicPath: PATHS.public
},
module: { rules: rules({ production: false, browser: true }) },
resolve,
plugins: plugins({ production: false })
};
Here are my JavaScript rules:
const presets = [["es2015", { "modules": false }], 'react', 'stage-0'];
const plugins = production ? [
'transform-react-remove-prop-types',
'transform-react-constant-elements',
'transform-react-inline-elements'
] : [];
return {
test: /\.js$|\.jsx$/,
loader: 'babel-loader',
options: {
presets,
plugins
},
exclude: PATHS.modules
};
Developer plugins:
if (!production) {
return [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.EnvironmentPlugin(['NODE_ENV']),
new webpack.DefinePlugin(compileTimeConstantForMinification),
new ExtractTextPlugin({
filename: '[contenthash].css',
allChunks: true
}),
// new webpack.optimize.UglifyJsPlugin({ compress }),
new ManifestPlugin({
fileName: 'manifest.json'
})
];
}
I'm assuming this is coming from a misunderstanding about how react-hot-loader works, but I cannot find any other information about why it wouldn't work in the docs or examples.
I know the pain of configuring this stuff.
You need to include the next lines in your entry section
entry: {
app: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./index.js'
]
}
then when you are setting babel-loader configuration you need to add react-hot-loader/babel
const plugins = production ? [
'transform-react-remove-prop-types',
'transform-react-constant-elements',
'transform-react-inline-elements'
] : ['react-hot-loader/babel'];
Here I have a simple configuration with this feature.
Hope it helps.

React do not render new components, new webpack setup

Today I was setting up my first Webpack Bebel React project and I got some strange case here.I don't know why, but every single Component that I make is not recognized by React. I can see it directly in the inspector, and It seems like it's not getting compiled. All standard HTML elements are getting rendered. Even console.log inside of constructor function of a component that I have created is not called. I run Hot mode with webpack -p
Here is my Webpack config:
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const webpack = require('webpack')
const path = require('path')
const isProduction = process.env.NODE_ENV === 'production'
const cssDeveloperLoaders = ['style-loader', 'css-loader', 'sass-loader']
const cssProduction = ExtractTextPlugin.extract({
fallback: 'style-loader',
loader: ['css-loader', 'sass-loader'],
publicPath: '/dist'
})
const cssConfig = isProduction ? cssProduction : cssDeveloperLoaders
module.exports = {
entry: {
app: './src/app.jsx'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /\.scss$/,
use: cssConfig
},
{
test: /\.jsx$/,
exclude: path.resolve(__dirname + '/node_modules/'),
use: 'babel-loader',
query: {
presents: ['es2015','react']
}
}
]
},
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
hot: true,
open: true,
openPage: '' //Fix to webpack version 3.0.0 after removing redirection to /undefined
},
plugins: [
new ExtractTextPlugin({
filename: 'app.css',
disable: !isProduction, //So if production is running it will generate file otherwise not
allChunks: true
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin()
]
}
My .bablerc
{
"presets": [
"es2015",
"react"
]
}
App.jsx:
import './app.scss'
import React from 'react';
import { render } from 'react-dom';
import engine from './engine.jsx'
render(
<engine/>,
document.getElementById('root')
);
engine.jsx
import React from 'react';
class engine extends React.Component{
constructor(){
super();
console.log('Component has been constructed ')
}
render(){
return(
<div>xD</div>
)
}
}
export default engine;
The picture of React Chrome extension.
Please notice, console.log is not been called.
My html is empty, I see only engine element (Not compiled.)
Any suggestions about this problem?
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="root"></div>
<script src="app.bundle.js"></script>
</body>
</html>
In your webpack config file add
resolve : {
extensions: [".js", ".jsx"]
}
So that you won't need to import your jsx file with extenstion.
Class names should start with Capital letters otherwise methods in react components will not be invoked and also no error will be thrown.
engine.jsx
class Engine extends React.Component{
constructor(){
super();
console.log('Component has been constructed ')
}
render(){
return(
<div>xD</div>
)
}
}
export default Engine;
App.jsx
import Engine from './engine'
render(
<Engine/>,
document.getElementById('root')
);
Please verify https://codesandbox.io/s/D9rpvWWG6
Also you can refer https://github.com/facebook/react/issues/4695

Categories

Resources