so i'm trying to setup server side rendering with my React app and I have solved every problem so far but I have run into this one which I cannot solve after a few days of trying.
I'm using the latest of everything included and I run into this error: Error: Invariant failed: You should not use <Switch> outside a <Router>
Below is the react app index
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
const SSR = (
<BrowserRouter>
<App/>
</BrowserRouter>
);
if(typeof document === "undefined") {
module.exports = SSR;
} else {
ReactDOM.hydrate(SSR, document.getElementById("app"));
}
And the component
import React from 'react';
import { Route, Switch } from 'react-router-dom'
import Home from './components/index'
const NavRoute = ({exact, path, component: Component}) => (
<Route exact={exact} path={path} render={(props) => (
<React.Fragment>
<Navbar/>
<Component {...props}/>
</React.Fragment>
)}/>
)
export default class App extends React.Component {
render() {
return (
<Switch>
<NavRoute exact={true} path="/" component={Home}/>
</Switch>
);
}
}
And my webpack config
const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
module.exports = [
{
entry: {
client: './server/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: "[name].js",
publicPath: '/',
},
target: 'node',
externals: [nodeExternals()],
module: {
rules: [
{ test: /\.jsx?/, loader: "babel-loader"}
]
},
},
{
entry: {
bundle: './src/App.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: "[name].js",
publicPath: '/',
libraryTarget: "umd",
globalObject: "this",
},
module: {
rules: [
{ test: /\.jsx?/, loader: "babel-loader"}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.BROWSER': JSON.stringify(true),
}),
]
}
]
any help is appreciated let me know if I missed any info out, thank you.
Related
How do I navigate to a specific page with url in React?
I realized I've used CRA in the past and haven't really tackled this specifically.
Currently I'm rendering ReactDOM with BrowserRouter and Switch with exact path in Routes
// index.jsx
/* eslint-disable import/extensions */
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './app/App.jsx';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'),
);
// App.jsx
import React from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import Header from '../components/Header';
import HomePage from '../pages/Home';
import PlansPage from '../pages/Plans';
import NotFoundPage from '../pages/NotFound';
const App = () => (
<BrowserRouter>
<Header />
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/plans" component={PlansPage} />
<Route component={NotFoundPage} />
</Switch>
</BrowserRouter>
);
export default App;
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
module.exports = {
devtool: false,
module: {
rules: [
{
test: /\.m?(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.scss$/,
use: [{
loader: 'style-loader',
options: {
sourceMap: true,
},
}, {
loader: 'css-loader',
options: {
sourceMap: true,
},
}, {
loader: 'sass-loader',
options: {
sourceMap: true,
},
}],
},
],
},
entry: './src/index.jsx',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js',
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: './index.html',
}),
new webpack.SourceMapDevToolPlugin({}),
],
};
Navigating to http://localhost:8080/plans leads to a page with Cannot GET /plans. However, clicking on a Link element navigates to that plans page without an issue.
Yeah since you're using webpack Dev Server all you have to do is add historyApiFallback=true and it'll fix you issue. You're essentially telling all routes to fall back to your index.html file. Hope that helped.
Here's my App.jsx that's first mounted to the DOM:
import React, { Component } from 'react';
import Home from './Home.jsx';
import Representatives from './Representatives.jsx';
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
export default class App extends Component {
constructor() {
super();
this.state = {value: ''};
}
render() {
return (
<Router>
<div>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/representatives" component={Representatives} />
</Switch>
</div>
</Router>
)
}
}
And here's the representative's component:
import React, { Component } from 'react';
export default class Representatives extends Component {
constructor() {
super();
this.state = {value: ''};
}
render() {
return (
<div>
Hello World!
</div>
)
}
}
It looks exactly like examples I've seen and my webpack config looks like other's I've seen in similar posts. Any help would be greatly appreciated, I'm at 4+ hours wracking my brain
Here's my webpack config:
const path = require('path');
const SRC_DIR = path.resolve(__dirname, './src');
const BUILD_DIR = path.resolve(__dirname, './public/');
module.exports = {
mode: 'development',
entry: path.resolve(SRC_DIR, 'index.jsx'),
output: {
filename: 'bundle.js',
path: BUILD_DIR,
publicPath: '/'
},
devServer: {
historyApiFallback: true,
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: [/node_modules/],
use: [{
loader: 'babel-loader',
options: { "presets": [
"#babel/env",
"#babel/react"
] }
}],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
]
}
}
I added the historyApiFallback: true, and publicPath: '/'
as recommended by another post here.
Here is the index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App/App.jsx';
ReactDOM.render(<App />, document.getElementById('root'));
i don't know what is diffrence in my code.
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const AsyncChunkNames = require('webpack-async-chunk-names-plugin');
const lunaRocketModulesPath = path.resolve(__dirname, 'luna-rocket');
module.exports = {
entry: [
'#babel/polyfill',
path.join(__dirname,'src/app','app.js')
],
output: {
path: path.join(__dirname,'build'),
filename: 'index.bundle.js',
chunkFilename: '[name].bundle.js',
publicPath: '/', // 헐랭이.. 이 게 뭐길래...
},
mode: process.env.NODE_ENV || 'development',
resolve: {
alias: {
'luna-rocket': lunaRocketModulesPath
},
extensions: [
'.js',
],
},
devServer: {
contentBase: path.join(__dirname,'src'),
disableHostCheck: true,
historyApiFallback: true
},
module: {
rules: [
{
// this is so that we can compile any React,
// ES6 and above into normal ES5 syntax
test: /\.(js)$/,
// we do not want anything from node_modules to be compiled
exclude: /node_modules/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.(css|scss)$/,
use: [
"style-loader", // creates style nodes from JS strings
"css-loader", // translates CSS into CommonJS
"sass-loader" // compiles Sass to CSS, using Node Sass by default
]
},
{
test: /\.(jpg|jpeg|png|gif|mp3|svg)$/,
loaders: ['file-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname,'src','index.html'),
inject: 'body',
}),
// new AsyncChunkNames()
],
optimization: {
splitChunks:{
cacheGroups: {
default: false,
vendors: false,
vendor: {
name: 'vender',
chunks: "all",
test: "/node_module/",
priority: 20
},
common: {
name: 'common',
minChunks: 2,
chunks: "all",
priority: 10,
reuseExistingChunk: true,
enforce: true
}
}
}
}
};
app.js
import React, { Suspense, lazy } from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Route, Switch, HashRouter, withRouter, useRouterHistory , Redirect} from 'react-router-dom'
import Home from './Home';
import RocketComponent from './RocketComponent';
const Loading = () => <div>loading...</div>
ReactDOM.render(
<Router>
<div className="app">
<div className="container">
<Suspense fallback={<Loading />}>
<Route exact path="/" component={Home} />
<Route path="/rocket" component={RocketComponent} />
</Suspense>
</div>
</div>
</Router>,
document.getElementById('app')
);
RocketComponent.js
import HeaderNew from "./HeaderNew";
import React, {lazy, Suspense} from 'react';
import RocketMenuNew from "./RocketMenuNew";
import {Route, Switch} from "react-router-dom";
function scrollToTop() {
document.body.scrollTop = 0
}
const menuData = [
{
title: "LUXAccordion",
path: "/rocket/LUXAccordion",
component: "./Documents/LUXAccordion/AccordionDocument"
},
{
title: "LUXActionBar",
path: "/rocket/LUXActionBar",
component: "./Documents/LUXActionBar/ActionBarDocument"
},
{
title: "LUXBadge",
path: "/rocket/LUXBadge",
component: "./Documents/LUXBadge/BadgeDocument"
},
{
title: "LUXButton",
path: "/rocket/LUXButton",
component: "./Documents/LUXButton/ButtonDocument"
}
]
function DynamicLoader(props) {
// console.log("title", `./Documents/${title.title}/${title.title.substring(3)}Document`)
// const LazyComponent = React.lazy(() => import(`./Documents/${title.title}/${title.title.substring(3)}Document`));
const LazyComponent = lazy(() => import(`${props.component}`));
console.log("LazyComponent", LazyComponent)
return (
<LazyComponent />
);
}
class RocketComponent extends React.Component {
render() {
console.log("this.props.match.path", this.props.match.path)
return (
<div className="documents-new">
<HeaderNew />
<RocketMenuNew />
<Switch>
{menuData.map((props, i) => {
return <Route path={props.path} render={() => <DynamicLoader component={props.component}/>} key={i}/>
})}
</Switch>
</div>
);
}
}
export default RocketComponent
this code is working.
but RocketComponent.js moves to the route directory. is not working.
i don't know why??
RocketComponent.js --> path: app/router/RocketComponent.js
import HeaderNew from "./../HeaderNew";
import React, {lazy, Suspense} from 'react';
import RocketMenuNew from "./../RocketMenuNew";
import {Route, Switch} from "react-router-dom";
function scrollToTop() {
document.body.scrollTop = 0
}
const menuData = [
{
title: "LUXAccordion",
path: "/rocket/LUXAccordion",
component: "./../Documents/LUXAccordion/AccordionDocument"
},
{
title: "LUXActionBar",
path: "/rocket/LUXActionBar",
component: "./../Documents/LUXActionBar/ActionBarDocument"
},
{
title: "LUXBadge",
path: "/rocket/LUXBadge",
component: "./../Documents/LUXBadge/BadgeDocument"
},
{
title: "LUXButton",
path: "/rocket/LUXButton",
component: "./../Documents/LUXButton/ButtonDocument"
}
]
function DynamicLoader(props) {
const LazyComponent = lazy(() => import(`${props.component}`));
return (
<LazyComponent />
);
}
class RocketComponent extends React.Component {
render() {
return (
<div className="documents-new">
<HeaderNew />
<RocketMenuNew />
<Switch>
{menuData.map((props, i) => {
return <Route path={props.path} render={() => <DynamicLoader component={props.component}/>} key={i}/>
})}
</Switch>
</div>
);
}
}
export default RocketComponent
i modify the component path and app.js modify RocketComponent path right.
but is not working
the error is
why is not working, the path is right, please help me .
my webpack is 4, babel 7
I have a React app, using webpack. I have added React Router 3 and when trying to visit a route in my app, I get a 404 on the .JS files when on anything but the initial route.
My application is also using a basename as I must prepend the application path.
Routes
import React from 'react'
import Loadable from 'react-loadable'
import { Router, Route, IndexRoute, IndexRedirect } from 'react-router'
import App from './components/App'
const AsyncRoute = loader =>
Loadable({
loader,
loading: () => <h3>Loading...</h3>,
delay: 300,
})
const LandingPage = AsyncRoute(() => import(/* webpackPrefetch: true, webpackChunkName: "landingPage" */ './containers/LandingPage'))
export default ({ history }) => (
<Router history={history}>
<Route path="/:tenant" component={App}>
<IndexRoute component={LandingPage} />
<Route path="foo" component={LandingPage} />
</Route>
</Router>
)
Index
import React, { Fragment } from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { useRouterHistory } from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'
import createBrowserHistory from 'history/lib/createBrowserHistory'
import store from './store'
import Router from './routes'
const historyConfig = { basename: '/content-manager' }
const browserHistory = useRouterHistory(createBrowserHistory)(historyConfig)
const history = syncHistoryWithStore(browserHistory, store)
render(
<Provider store={store}>
<Fragment>
<Router history={history} />
</Fragment>
</Provider>,
document.getElementById('root')
)
Webpack.dev
const PORT = process.env.SERVER_PORT || 3000
const HOST = process.env.SERVER_HOST || '127.0.0.1'
module.exports = {
devtool: 'inline-source-map',
mode: 'development',
output: {
filename: '[name].[hash].js',
publicPath: '',
},
devServer: {
host: 'localhost',
port: process.env.PORT || 3200,
historyApiFallback: true,
hot: false,
open: true,
proxy: {
'/api/**': {
target: `http://${HOST}:${PORT}`,
pathRewrite: { '^/api': '' },
secure: false,
logLevel: 'debug',
},
},
},
}
Webpack.common
const commonPaths = require('../common-paths');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const Dotenv = require('dotenv-webpack');
module.exports = {
context: commonPaths.appPath,
entry: ['babel-polyfill', './index.jsx'],
resolve: {
extensions: ['.js', '.jsx'],
},
module: {
rules: [
{ test: /\.(jsx?)$/, exclude: /node_modules/, use: ['babel-loader'] },
],
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
chunks: 'initial',
test: 'vendor',
name: 'vendor',
enforce: true,
},
},
},
},
plugins: [
new HtmlWebpackPlugin({
title: 'Web App',
template: commonPaths.projectRoot + '/public/index.html',
inject: 'body',
}),
new CompressionPlugin({
algorithm: 'gzip',
test: /\.js$|\.css$/,
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: process.env.NODE_ENV === 'prod',
}),
new Dotenv(),
],
};
If I visit http://app.local/content-manager/foo in the browser, the landing page will load.
If I then trying and navigate to http://app.local/content-manager/foo/edit the application does not load and I see the following in the console
GET http://app.local/content-manager/foo/main.fdb34ff55bc02a8cd347.js 404 (Not Found)
edit:1 Refused to execute script from 'http://app.local/content-manager/foo/main.fdb34ff55bc02a8cd347.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
I believe on the initial route, it is trying to find the js bundle at http://app.local/content-manager but then when I move to a sub route it is looking in http://app.local/content-manager/foo
So I have added
<base href="/content-manager/" />
into my index.html and now it seems to work....
hey guys having trouble with react-router, i keep getting this unexpected token you may need an appropriate loader to handle this file type on the line with <Router history={browserHistory}>. im not sure what is going on here any help would be appreciated!
thanks
import React from 'react'
import ReactDOM from 'react-dom'
import { Router, Route, browserHistory } from 'react-router'
import Products from './Products'
import Home from './Home'
document.addEventListener('DOMContentLoaded', function() {
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={Home} />
<Route path="/" component={Products} />
</Router>, document.getElementById('mount')
);
});
webpack.config
var path = require('path');
var config = {
context: path.join(__dirname, 'src/js'),
entry: [
'./main.js',
],
output: {
path: path.join(__dirname, 'www'),
filename: 'bundle.js',
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loaders: ['babel'],
},
],
},
resolve: {
root: [
path.join(__dirname, 'node_modules'),
],
},
};
module.exports = config;
.babelrc
{
"presets": ["es2015", "react"]
}
This isn't a React Router issue but instead is an issue with your JSX transpilation. Check your webpack.config.js file or your .babelrc file.