I have components/Header/index.jsx looking like this:
import React from 'react';
import PropTypes from 'prop-types';
import Breadcrumb from '../Breadcrumb';
// import styled from 'styled-components';
import Logo from '../Logo';
/* eslint-disable react/prefer-stateless-function */
class Header extends React.Component {
render() {
const providerId = (this.props.profileData.profileData.length > 0) ? this.props.profileData.profileData[0].provider_id : null;
if (!providerId) {
return "Loading...";
}
const certifStatus = (this.props.profileData.profileData.length > 0) ? this.props.profileData.profileData[0].certification_status : null;
let showInfo = false;
if (certifStatus === 'certified'){
showInfo = true;
}
return (
<div className="header">
<div className="header__top">
<div className="container-fluid">
<div className="row">
<div className="col-12">
<a href="/" className="header__logo">
<Logo providerId={providerId} />
</a>
<span style={{ marginLeft: '4px' }} className="header__title">
{this.props.text}
</span>
</div>
</div>
</div>
</div>
<Breadcrumb text="Artist certification" link="https://www.believebackstage.com/" showInfo={showInfo} infoLink="#"/>
</div>
);
}
}
Header.propTypes = {
profileData: PropTypes.object,
text: PropTypes.string,
};
export default Header;
When I try to import it into containers/ProfilePage/index.js
import Header from '../../components/Header/index.jsx';
It throws:
ERROR in ./app/components/Header/index.jsx 28:6
Module parse failed: Unexpected token (28:6)
You may need an appropriate loader to handle this file type.
| }
| return (
> <div className="header">
| <div className="header__top">
| <div className="container-fluid">
# ./app/containers/ProfilePage/index.js 30:0-55 69:28-34
# ./app/containers/ProfilePage/Loadable.js
# ./app/containers/App/index.js
# ./app/app.js
# multi eventsource-polyfill webpack-hot-middleware/client?reload=true ./app/app.js
It seems as if it is a webpack issue, so here is how my internals/webpack/webpack.base.babel.js looks like:
/**
* COMMON WEBPACK CONFIGURATION
*/
const path = require('path');
const webpack = require('webpack');
// Remove this line once the following warning goes away (it was meant for webpack loader authors not users):
// 'DeprecationWarning: loaderUtils.parseQuery() received a non-string value which can be problematic,
// see https://github.com/webpack/loader-utils/issues/56 parseQuery() will be replaced with getOptions()
// in the next major version of loader-utils.'
process.noDeprecation = true;
module.exports = options => ({
mode: options.mode,
entry: options.entry,
output: Object.assign(
{
// Compile into js/build.js
path: path.resolve(process.cwd(), 'build'),
publicPath: '/',
},
options.output,
), // Merge with env dependent settings
optimization: options.optimization,
module: {
rules: [
{
test: /\.(js|jsx)$/, // Transform all .js files required somewhere with Babel
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: options.babelQuery,
},
},
{
// Preprocess our own .css files
// This is the place to add your own loaders (e.g. sass/less etc.)
// for a list of loaders, see https://webpack.js.org/loaders/#styling
test: /\.scss$/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
// Preprocess 3rd party .css files located in node_modules
test: /\.css$/,
include: /node_modules/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(eot|otf|ttf|woff|woff2)$/,
use: 'file-loader',
},
{
test: /\.svg$/,
use: [
{
loader: 'svg-url-loader',
options: {
// Inline files smaller than 10 kB
limit: 10 * 1024,
noquotes: true,
},
},
],
},
{
test: /\.(jpg|png|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// Inline files smaller than 10 kB
limit: 10 * 1024,
},
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
enabled: false,
// NOTE: mozjpeg is disabled as it causes errors in some Linux environments
// Try enabling it in your environment by switching the config to:
// enabled: true,
// progressive: true,
},
gifsicle: {
interlaced: false,
},
optipng: {
optimizationLevel: 7,
},
pngquant: {
quality: '65-90',
speed: 4,
},
},
},
],
},
{
test: /\.html$/,
use: 'html-loader',
},
{
test: /\.(mp4|webm)$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
},
},
},
],
},
plugins: options.plugins.concat([
new webpack.ProvidePlugin({
// make fetch available
fetch: 'exports-loader?self.fetch!whatwg-fetch',
}),
// Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
// inside your code for any environment checks; UglifyJS will automatically
// drop any unreachable code.
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
},
}),
]),
resolve: {
modules: ['node_modules', 'app'],
extensions: ['.js', '.jsx', '.react.js'],
mainFields: ['browser', 'jsnext:main', 'main'],
},
devtool: options.devtool,
target: 'web', // Make web variables accessible to webpack, e.g. window
performance: options.performance || {},
});
I've tried solutions from several similar questions like this one.
Note:
I'm using this react boilerpalte.
Please help me unstuck folks.
After having looked at the docs a little closer here is how I got it to work...
Take a look here: https://github.com/react-boilerplate/react-boilerplate/tree/master/docs/js
They are using plop to autogenerate components containers etc...
So you simply do: npm run generate and follow the prompts...
I did and created a container called ProfilePage...
In my ProfilePage I imported the Header (the rest it simply auto-generated, the only thing I added was the import statement:
Here it is:
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
import { FormattedMessage } from 'react-intl';
import { createStructuredSelector } from 'reselect';
import { compose } from 'redux';
// Here is my import (notice no relative path needed)
import Header from 'components/Header';
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';
import makeSelectProfilePage from './selectors';
import reducer from './reducer';
import saga from './saga';
import messages from './messages';
/* eslint-disable react/prefer-stateless-function */
export class ProfilePage extends React.Component {
render() {
return (
<div>
<Helmet>
<title>ProfilePage</title>
<meta name="description" content="Description of ProfilePage" />
</Helmet>
<FormattedMessage {...messages.header} />
// Here is where I render it
<Header />
</div>
);
}
}
ProfilePage.propTypes = {
dispatch: PropTypes.func.isRequired,
};
const mapStateToProps = createStructuredSelector({
profilepage: makeSelectProfilePage(),
});
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
const withReducer = injectReducer({ key: 'profilePage', reducer });
const withSaga = injectSaga({ key: 'profilePage', saga });
export default compose(
withReducer,
withSaga,
withConnect,
)(ProfilePage);
That whole thing with the exception of the import Header from 'components/Header' and the rendering of header <Header /> just below <FormattedMessage {...messages.header} /> was auto-generated using plop
Then I simply imported ProfilePage in my App/index.js and added a route... Now when I navigate to localhost:3000/profile the ProfilePage shows up along with the Header...
Here is the App/index.js:
import React from 'react';
import { Helmet } from 'react-helmet';
import styled from 'styled-components';
import { Switch, Route } from 'react-router-dom';
import HomePage from 'containers/HomePage/Loadable';
import FeaturePage from 'containers/FeaturePage/Loadable';
import NotFoundPage from 'containers/NotFoundPage/Loadable';
import Header from 'components/Header';
import Footer from 'components/Footer';
// Here is where I import it...
import ProfiePage from 'containers/ProfilePage';
const AppWrapper = styled.div`
max-width: calc(768px + 16px * 2);
margin: 0 auto;
display: flex;
min-height: 100%;
padding: 0 16px;
flex-direction: column;
`;
export default function App() {
return (
<AppWrapper>
<Helmet
titleTemplate="%s - React.js Boilerplate"
defaultTitle="React.js Boilerplate"
>
<meta name="description" content="A React.js Boilerplate application" />
</Helmet>
<Header />
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/features" component={FeaturePage} />
// Here is the route I added...
<Route path="/profile" conmponent={ProfiePage} />
<Route component={NotFoundPage} />
</Switch>
<Footer />
</AppWrapper>
);
}
In conclusion, if you're going to use this boilerplate you absolutely need to go through their docs...
Related
I have this app which I'm rendering on the server side. Everything was working fine until I tried to add Material UI to it.
My directory structure is this:
app/
build/ * This is created by webpack
server_bundle.js
public/
client_bundle.js
fonts/
images/
src/
client/
Client.tsx
server/
server.tsx
shared/
App.tsx
routes.tsx
webpack.config.js
And here are my files content:
webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
target: 'node',
entry: {
server: path.resolve(__dirname, 'src/server/server.tsx'),
"public/client": path.resolve(__dirname, 'src/client/client.tsx')
},
output: {
filename: '[name]_bundle.js',
path: path.resolve(__dirname, 'build'),
publicPath: '/build'
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
},
module: {
rules: [{
test: /\.(tsx|ts)?$/,
loader: 'awesome-typescript-loader',
options: {
jsx: 'react'
}
},
{
test: /\.(scss|sass|css)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
]
},
{
test: /\.(ico)$/,
loader: 'file-loader',
options: { outputPath: '/public', publicPath: '/public', name: '[name].[ext]' }
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
loader: 'file-loader',
options: { outputPath: '/public/images', publicPath: 'images' }
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
loader: 'file-loader',
options: { outputPath: '/public/fonts', publicPath: 'fonts' }
},
]
},
optimization: {
},
plugins: [
new MiniCssExtractPlugin({
filename: 'public/styles_bundle.css',
chunkFilename: "public/styles/[id].css"
})
]
}
server.tsx
import * as express from "express";
import * as bodyParser from "body-parser";
import * as React from "react";
import * as ReactDOMServer from "react-dom/server";
import {StaticRouter} from "react-router";
import { matchPath } from "react-router-dom";
import {Helmet} from "react-helmet";
import App from "../shared/App";
import routes from '../shared/routes';
const app = express();
const PORT = process.env.PORT || 3000;
app.use(bodyParser.urlencoded());
app.use(bodyParser.json());
app.use(express.static("build/public"));
app.get('*', (req, res, next) => {
const activeRoute = routes.find(route => !!matchPath(req.url, route)) || {path: "/"};
const now = new Date();
console.log(`GET ${now} - ${req.url}`);
const context = {}
const content = ReactDOMServer.renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
const helmet = Helmet.renderStatic();
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
${helmet.title.toString()}
${helmet.meta.toString()}
<link rel="stylesheet" href="styles_bundle.css">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
</head>
<body>
<div id="root" style="overflow-x: hidden; width: 100%; margin: 0;">${content}</div>
<script src="client_bundle.js"></script>
</body>
</html>
`;
res.send(html);
});
app.listen(PORT, () => {
console.log(`App is running on port ${PORT}`)
})
Client.tsx
import * as React from "react";
import * as ReactDOM from 'react-dom';
import { BrowserRouter } from "react-router-dom";
import App from "../shared/App";
// Because it's already been rendered, we only need to hydrate event
// handlers and wire things up.
ReactDOM.hydrate(
<BrowserRouter>
<App />
</BrowserRouter>,
document.querySelector("#root")
);
App.tsx
import * as React from 'react';
import routes from "../shared/routes";
import { Helmet } from 'react-helmet';
import { Switch, Route } from "react-router";
import 'typeface-roboto';
class App extends React.Component {
render() {
return (
<React.Fragment>
<Switch>
{routes.map(({path, exact, component: C}) => {
return <Route
path={path}
exact={exact}
render={(props) => <C {...props}/> }
/>
})}
</Switch>
</React.Fragment>
)
}
}
export default App;
And finally, routes.tsx
import * as React from 'react';
import { Button } from '#material-ui/core';
const routes = [
{
name: "Home",
exact: true,
path: "/",
component: (props:any) => {return (
<Button variant="contained" color="primary">
Hello World
</Button>
)}
}
];
export default routes;
I get this error in my browser console and obviously no material ui styles whatsoever:
client_bundle.js:44 Uncaught ReferenceError: global is not defined
at Object.<anonymous> (client_bundle.js:44)
at n (client_bundle.js:1)
at Object.<anonymous> (client_bundle.js:1)
at n (client_bundle.js:1)
at Object.<anonymous> (client_bundle.js:17)
at n (client_bundle.js:1)
at Object.<anonymous> (client_bundle.js:17)
at n (client_bundle.js:1)
at Object.<anonymous> (client_bundle.js:36)
at n (client_bundle.js:1)
That part of the client_bundle.js looks like this:
... __esModule",{value:!0});global.CSS;t.default=function(e){return e}} ...
What do you think might be happenning here??
try this workaround in your
plugins: [
new webpack.DefinePlugin({
'global': {} // webpack workaround
}),
new MiniCssExtractPlugin({
filename: 'public/styles_bundle.css',
chunkFilename: "public/styles/[id].css"
})
]
I have been working on this for two days now. Looked through multiple stack posts and still not found a suitable answer.
I am trying to rendering my react project in server like following:
Server.js
function handleRender(req,res){
const sheetsRegistry = new SheetsRegistry();
const sheetsManager = new Map();
const theme = createMuiTheme({
palette:{
primary:green,
accent: red,
type: 'light',
}
})
const generateClassName = createGenerateClassName();
const html = ReactDOMServer.renderToString(
<JssProvider registry={sheetsRegistry} generateClassName={generateClassName}>
<MuiThemeProvider theme={theme} sheetsManager={sheetsManager}>
<TwoFA />
</MuiThemeProvider>
</JssProvider>
)
const css = sheetsRegistry.toString()
res.send(renderFullPage(html,css))
}
function renderFullPage(html,css){
return `
<!DOCTYPE html>
<html>
<head>
<title>2FA SDK</title>
</head>
<body style="margin:0">
<div id="app">${html}</div>
<script id="jss-server-side">${css}</script>
</body>
</html>
`
}
Client.js:
import React from 'react';
import ReactDOM from 'react-dom';
import TwoFA from './App';
import {
MuiThemeProvider,
createMuiTheme,
createGenerateClassName,
} from '#material-ui/core/styles';
import green from '#material-ui/core/colors/green';
import red from '#material-ui/core/colors/red';
class Main extends React.Component{
componentDidMount() {
const jssStyles = document.getElementById('jss-server-side');
if (jssStyles && jssStyles.parentNode) {
jssStyles.parentNode.removeChild(jssStyles);
}
}
render(){
return <TwoFA />
}
}
const theme = createMuiTheme({
palette: {
primary: green,
accent: red,
type: 'light',
},
});
const generateClassName = createGenerateClassName();
if (typeof window !== 'undefined'){
ReactDOM.hydrate(
<JssProvider generateClassName={generateClassName}>
<MuiThemeProvider theme={theme}>
<TwoFA/>
</MuiThemeProvider>
</JssProvider>,
document.querySelector('#app'),
);
}
Webpack.config.js
module.exports = [
{
/*Config for backend code*/
entry: './src/server/server.js',
output: {
filename: 'server.js'
},
externals: [nodeExternals()],
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"
})
]
},
{
entry: './src/client.js',
output: {
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
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"
})
]
}
]
What I have tried: I search on SO and found that many posts suggesting put a condition check like so : if (typeof window !== 'undefined'). However, this does not solve the problem.
I also understood that the error is due to the fact that during SSR, server-side has no document project.
I have searched on github issue page and someone mentioned that he ran into the problem with webpack, but same project worked fine with browserify.
What I need help with: I am trying to solve this problem as it cause the app to break.
I am suspecting that there is something wrong with webpack. I am looking a fix for this
This issue usually happens because when react is rendered on the server. It does not have a document or window object on the server side and those objects are only available on the browser.
Try to call the document functions in or after componentDidMount.
componentDidMount(){
this.setState({documentLoaded:true});
}
someFunction(){
const { documentLoaded } = this.state;
if(documentLoaded){
// LOGIC USING DOCUMENT OBJECT
}
}
If you are using react-hooks, you can create your custom useDocument hook:
import React, { useEffect, useState } from 'react'
export const useDocument = () => {
const [myDocument, setMyDocument] = useState(null)
useEffect(() => {
setMyDocument(document)
}, [])
return myDocument
}
in your component:
...
const doc = useDocument()
...
<SomeComponent
ref={doc && doc.body}
...
/>
...
I am not sure if react router is not working correctly or if I am missing something.
I have something like this
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'mobx-react';
import { configure } from 'mobx';
import createBrowserHistory from 'history/createBrowserHistory';
import {syncHistoryWithStore } from 'mobx-react-router';
import { Router } from 'react-router'
import AppContainer from './components/App';
const browserHistory = createBrowserHistory();
import stores from '../src/stores/Stores';
const history = syncHistoryWithStore(browserHistory, stores.routingStore);
configure({ enforceActions: true});
ReactDOM.render(
<Provider {... stores}>
<Router history={history}>
<AppContainer />
</Router>
</Provider>,
document.getElementById('app')
);
Then in my AppContainer I have this
import { withRouter, Route, Link } from "react-router-dom";
<Route path="/company-details/company/:companyId/employee/:employeeId" component={CompanyComponent} />
and
<Link to="/company-details/company/76/employee/77"></Link>
now when I click on the link, it goes to the right page and I got access to the parameters.
but say if I did ctrl + click to make a new tab while clicking on the link or refreshing the page.
I get
GET http://localhost:8080/company-details/company/76/employee/index_bundle.js 404 (Not Found)
Refused to execute script from 'http://localhost:8080/company-details/company/76/employee/index_bundle.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
I have these packages installed
"react-router-dom": "^4.2.2"
"mobx-react-router": "^4.0.4",
Edit
my webpack.config
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: ["babel-polyfill", "./src/index.js"],
output: {
path: path.join(__dirname, "/dist"),
filename: "index_bundle.js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /(\.css|\.scss|\.sass)$/,
use: [
{
loader: "style-loader" // creates style nodes from JS strings
},
{
loader: "css-loader" // translates CSS into CommonJS
},
{
loader: "sass-loader" // compiles Sass to CSS
}
]
},
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
use: [
{
loader: 'file-loader',
options: {}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
})
],
devServer: {
historyApiFallback: true
}
};
Inside index.html you should link your script file like following:
<script type="text/javascript" src="./index_bundle.js"></script>
I am using Webpack 3.7.1 and React 15.6.1 and I am trying to load different components dynamically.
What I would like to do
Loading the components asynchronously from the different chunks webpack created when code splitting
What i did
Using getComponent() and import() to generate the chunks
Configured the webpack.config file properly so that the chunks are created (code splitting)
The issue
Chunks are generated but not loaded properly when accessing a route
getComponent() does not seem to work
My Webpack.config file
module.exports = {
devServer: {
historyApiFallback: true
},
entry: {
app:"./src/index.js",
vendor: [
"axios",
"react",
"react-dom",
"react-redux",
"react-router",
"react-router-dom",
"redux"
]
},
output: {
path: __dirname + '/public/views',
filename: '[name].js',
chunkFilename: '[chunkhash].chunk.js',
publicPath: "/views/"
},
module: {
loaders: [
{
test: /\.js$/,
loader: "babel-loader",
exclude: [/node_modules/, /pdfmake.js$/]
},
{
test: /\.json$/,
loader: "json-loader"
}
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
minChunks: Infinity
}),
new webpack.NamedModulesPlugin(),
new HtmlWebpackPlugin({
filename: __dirname + "/views/index.ejs",
template: __dirname + "/views/template.ejs",
inject: 'body',
chunks: ['vendor', 'app'],
chunksSortMode: 'manual'
}),
new PreloadWebpackPlugin({
rel: "preload",
include: ["vendor", "app"]
}),
new webpack.optimize.OccurrenceOrderPlugin(),
]
};
My index.js file (root of my react app)
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import promise from "redux-promise";
import reducers from "./reducers";
import AppInit from "./containers/appInit";
import ProfRegisteringModal from "./containers/modals/register_prof_explanation_modal";
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
function errorLoading(err) {
console.error("Dynamic page loading failed", err);
}
function loadRoute(cb) {
return module => cb(null, module.default);
}
console.log("testst");
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<AppInit>
<BrowserRouter>
<div style={{ height: "100%" }}>
<ProfRegisteringModal />
<Switch>
<Route
path="/inscription/:user"
getComponent={(location, callback) => {
import(
"./components/registering/registering_landing_page.js"
)
.then(loadRoute(cb))
.catch(errorLoading);
}}
/>
<Route
path="/inscription"
getComponent={(location, callback) => {
import(
"./components/registering/registering_landing_page.js"
)
.then(loadRoute(cb))
.catch(errorLoading);
}}
/>
<Route
path="/connexion"
getComponent={(location, callback) => {
import("./containers/registering/signing_in.js")
.then(loadRoute(cb))
.catch(errorLoading);
}}
/>
<Route
path="/equipe"
getComponent={(location, callback) => {
import("./components/team_pres.js")
.then(loadRoute(cb))
.catch(errorLoading);
}}
/>
<Route
path="/"
getComponent={(location, callback) => {
import("./containers/app_container.js")
.then(loadRoute(cb))
.catch(errorLoading);
}}
/>
</Switch>
</div>
</BrowserRouter>
</AppInit>
</Provider>,
document.querySelector(".root")
);
This file got correctly loaded as I can see the console.log("test") appearing in my console.
None of the components are correctly loaded when accessing any of the routes.
Thank you very much for your help
I think what your code is missing is a way to trigger an update.
I remember solving this issue by creating a wrapper around the import() promise.
// AsyncComponent.js
export default function wrapper(importComponent) {
class AsyncComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
Comp: null
};
}
componentDidMount() {
importComponent()
.then(Comp => this.setState({
Comp
}))
.catch(err => this.setState({
error: err
}));
}
render() {
if(this.state.error) {
return <h2> Loading error
<button onClick={e => this.componentDidMount()}> Try again </button>
</h2>
}
const Comp = this.state.Comp;
return Comp ?
<Comp {...this.props} /> :
<div> Still Loading: You can add a spinner here </div>
}
}
return AsyncComponent;
}
// Routes.js
import AsyncComponent from './component/AsyncComponent';
const Users = AsyncComponent(() => import(/* webpackChunkName:"users" */ './Users'))
const Home = AsyncComponent(() => import(/* webpackChunkName:"home" */ './Home'))
const Equipe = AsyncComponent(() => import(/* webpackChunkName:"equipe" */ './Equipe'))
<Route path='/users' component={Users} />
<Route path='/equipe' component={Equipe} />
I have a problem. I cant refresh my react components/page without getting "Cannot GET /currentPage". I've browsed you for some time now and found a couple of links that could be the solution of my issue:
https://github.com/jintoppy/react-training/blob/master/basic/node_modules/react-router/docs/guides/Histories.md#browserhistory
HashHistory of BrowserHistory. Internet said I should use BrowserHistory for production - but that hashHistory is easier. They are both so effing complicated. I cant for my life figure out how to implement it to my current code.
This is my app.js file:
/*global $:true*/
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';
import './scss/app.scss';
// Component imports
import Home from './components/home';
import Archive from './archive';
// Image import
import loadImg from './images/tits.gif';
class App extends Component {
// Function for anchorlinks
hashLinkScroll() {
const { hash } = window.location;
if (hash !== '') {
// Push onto callback queue so it runs after the DOM is updated, this is required when navigating from a different page so that the element is rendered on the page before trying to getElementById
setTimeout(() => {
const id = hash.replace('#', '');
const element = document.getElementById(id);
if (element) element.scrollIntoView();
}, 100);
}
}
// 1. Render site-loader gif
// 2. React Router component wraps all of the routes we are going to define - Archive and Home. Each route will be identified in a <Route> component. The <Route> component will take two properties: path and component. When a path matches the path given to the <Route> component, it will return the component specified.
render() {
return (
<div>
<div className="loaderSmall">
<img className="loadingImg" src={loadImg} width="400"/>
</div>
<Router history={browserHistory} onUpdate={this.hashLinkScroll}>
<Route path={'/archive'} component={Archive} />
<Route path={'*'} component={Home} />
</Router>
</div>
);
};
// When Component has rendered, window.addEventListener adds event "load" and calls handleLoad function
componentDidMount() {
window.addEventListener('load', this.handleLoad);
}
// Fade out site-loader
handleLoad() {
$(".loaderSmall").delay(500).fadeOut("slow");
}
};
ReactDOM.render (
<App/>,
document.getElementById('app')
)
// Hot Module Replacement API (injecting code)
if (module.hot) {
module.hot.accept();
}
export default App;
..this is my menu component that renders when I am on "/archive" component:
import React, { Component } from 'react';
import { Link } from 'react-router';
//Menu component renders menu Link
class Menu extends Component {
render() {
return (
<header>
<nav>
<ul>
<li><Link to={'/#top'}>Home</Link></li>
<li><Link to={'/#about'}>About</Link></li>
<li><Link to={'/archive'}>Archive</Link></li>
<li className="contactMobile">Contact</li>
<li className="contactWeb"><Link to={'/#contact'}>Contact</Link></li>
</ul>
</nav>
</header>
);
}
}
export default Menu;
..and this is my other menu that renders when i am on root where i want scrollable hashlinks:
import React, { Component } from 'react';
import { Link } from 'react-router';
import Scrollchor from 'react-scrollchor';
//Menu component renders menu Link
class MenuB extends Component {
render() {
return (
<header>
<nav>
<ul>
<li><Scrollchor to="#top" animate={{offset: 20, duration: 800}}>Home</Scrollchor></li>
<li><Scrollchor to="#about" animate={{offset: 0, duration: 800}}>About</Scrollchor></li>
<li><Link to={'/archive'}>Archive</Link></li>
<li className="contactMobile">Contact</li>
<li className="contactWeb"><Scrollchor to="#contact" animate={{offset: 20, duration: 800}}>Contact</Scrollchor></li>
</ul>
</nav>
</header>
);
}
}
export default MenuB;
my webpack.config.js file:
// DEVELOPMENT
const webpack = require('webpack');
const path = require('path');
const entry = [
'webpack-dev-server/client?http://localhost:8080', // bundle the client for webpack-dev-server and connect to the provided endpoint
'webpack/hot/only-dev-server', // bundle the client for hot reloading only- means to only hot reload for successful updates
'./app.js'
]
const output = {
path: path.join(__dirname, 'dist'),
publicPath: '/dist',
filename: 'bundle.min.js'
}
const plugins = [
new webpack.HotModuleReplacementPlugin(), // enable HMR globally
new webpack.NamedModulesPlugin() // prints more readable module names in the browser console on HMR updates
]
const config = {
context: path.join(__dirname, 'src'),
entry: entry,
output: output,
devtool: "inline-source-map",
module: {
rules: [
{
// test: /\.(js|jsx)$/,
// exclude: /node_modules/,
// include: path.join(__dirname, 'src'),
// use: {
// loader: "eslint-loader",
// options: {
// failOnWarning: false,
// failOnError: false
// }
// }
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
include: path.join(__dirname, 'src'),
use: {
loader: "babel-loader"
}
},
{
test: /\.(png|jpg|gif)$/,
use: [{
loader: 'url-loader',
options: { limit: 10000, name: './images/[name].[ext]' }
}]
},
{
test: /\.(sass|scss)$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}
]
},
performance: {
maxAssetSize: 400000000,
maxEntrypointSize: 400000000,
hints: 'warning'
},
plugins: plugins,
externals: {
jquery: 'jQuery'
}
}
module.exports = config
And my webpack.config.prod.js file:
// PRODUCTION
const webpack = require('webpack');
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const entry = {
app: path.join(process.cwd(), 'src/app.js')
}
const output = {
path: path.join(__dirname, 'dist'),
filename: 'bundle.min.js',
}
const plugins = [
new webpack.DefinePlugin({
// 'process.env.NODE_ENV': JSON.stringify('production')
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new webpack.optimize.UglifyJsPlugin({
mangle: false,
compress: {
warnings: false
}
}),
new ExtractTextPlugin('bundle.css'), // creation of HTML files to serve your webpack bundles
new HtmlWebpackPlugin({
template: 'index-template.html'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'bundle',
filename: '[name].common.js'
})
]
const config = {
context: path.join(__dirname, 'src'),
entry: entry,
output: output,
devtool: "source-map",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
include: path.join(__dirname, 'src'),
use: "babel-loader"
},
{
test: /\.(png|jpg|gif)$/,
use: [{
loader: 'url-loader',
options: { limit: 10000, name: './images/[name].[ext]' } // Convert images < 10k to base64 strings (all in images folder)
}]
},
{
test: /\.(sass|scss)$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: (loader) => [ require('autoprefixer')() ]
}
},
'sass-loader',
]
})
}
]
},
plugins: plugins,
externals: {
jquery: 'jQuery'
}
}
module.exports = config;
..I know that there are much better ways to do this than to have two menu components that renders on different pages, but I just did this solution for now .. Problem is that I don't understans how to convert this to HashHistory of BrowserHistory without loosing my logic. Any tips/input would be so goddammit appreciated, been sitting with this for weeks now <3
Cannot GET /currentPage ?
For browserHistory on page refresh ,/currentPage is requested on server side.
At the backend, your server dont defined this path (requested resource).
You need to implement it to fixed Cannot GET issue for page refresh.
Assuming nodejs
app.use(express.static(__dirname));
//will serve index.html for every page refresh.
app.use('*',(req,resp)=>{
res.sendFile(path.resolve(__dirname+'/index.html'))
})
app.listen(someport)
This will load index.html page for every page refresh.
Once index.html is loaded with required JS & react router,
the router will trigger the route and corresponding component is getting rendered.
#Panther solved this. To be able to refresh page in my dev environment, I had to add:
historyApiFallback: {
disableDotRule: true
}
to my webpack dev.server file:
var WebpackDevServer = require('webpack-dev-server');
var webpack = require('webpack');
// requiring my webpack configuration
var config = require('./webpack.config.js');
var path = require('path');
var compiler = webpack(config);
// then spinning up a new dev server with some settings
var server = new WebpackDevServer(compiler, {
hot: true,
filename: config.output.filename,
publicPath: config.output.publicPath,
proxy: {
"/getMail": 'http://localhost:80/magdan/php/mailer.php',
"/getProducts": 'http://localhost:80/magdan/php/products.php'
},
stats: {
colors: true
},
historyApiFallback: {
disableDotRule: true
}
});
// its gonna listen to port 8080
server.listen(8080, 'localhost', function() {
console.log("Starting server on http://localhost:8080");
});