I have created build using Webpack 4.2.0. But I'm not able to access URLs directly without landing to index page.
My WebPack Config:
var webpack = require('webpack');
const HtmlPlugin = require('html-webpack-plugin')
// require('react-select/dist/react-select.css')
var path = require('path');
var BUILD_DIR = path.resolve(__dirname, 'build');
var APP_DIR = path.resolve(__dirname, 'src/');
var CSS_DIR = path.resolve(__dirname,'public/css');
// const HtmlPlugin = require('html-webpack-plugin');
var config = {
entry: APP_DIR + '/index.js',
output: {
path: BUILD_DIR,
filename: 'bundle.js'
},
plugins: [
// Configure HtmlPlugin to use our own index.html file
// as a template.
// Check out https://github.com/jantimon/html-webpack-plugin
// for the full list of options.
new HtmlPlugin({
template: 'public/index.html'
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
}),
// new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.LoaderOptionsPlugin({
minimize: true
})
],
module : {
rules: [
{
test: /\.js$/,
enforce: "pre",
include : APP_DIR,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
include : CSS_DIR
}
]
}
};
module.exports = config;
Index.js
render((
<BrowserRouter>
<App />
</BrowserRouter>
), document.getElementById('root'));
APP.JS
const Main = () => (
<main>
<Switch>
<Route exact path='/' component={Home}/>
<Route exact path='/contact' component={Contact}/>
<Route path='/listing' component={Listing}/>
<Route path='/product/:id' component={Product}/>
</Switch>
</main>
)
In Build folder, two files created.
- index.html
- bundle.js
And I have used http-server to open React Build
./node_modules/.bin/http-server build -p 3007
But In browser, it will throw 404 if I access URL localhost:3007/contact directly.
What is right way to deploy this for production which can be accessed via url say www.reactapp.com/contact
You need to configure your server to serve your index.html for all GET requests. ReactRouter just changes the navigation history, you don't actually leave the page when the route changes. Currently, your server is just serving the index.html page for the / route. Configure it to serve the page on all routes and you will be able to visit the routes by access their urls directly.
Related
I am trying to use a switch that routes different components depending on a URL. This URl should be able to be nested, like courses/:cid/assignments/:aid.
This is how my Switch looks like:
<Switch>
<Route path='/courses/:cid/assignments/:aid' component={Assignment} />
<Route exact path="/" component={Home} />
</Switch>
The app runs fine: I can access the home page. However, when I access for example courses/1/assignments/20, I get the following error:
Loading failed for the <script> with source “http://localhost:8081/courses/1/assignmentsets/bundle.js”.
I fiddled around a bit with the paths, just to see where it went wrong and these are the results:
<Switch>
<Route path='/courses/:cid/assignments/:aid' component={Assignments} /> // Does not work
<Route path="/courses/randomnestedurl" component={Assignments} /> // Does not work
<Route path="/courses" component={Assignments} /> // Works fine
<Route path="/:aid" component={Assignments} /> // Works fine
<Route exact path="/" component={Home} /> // Works fine
</Switch>
Does anyone have an idea what could be wrong? Something with my webpack? Some setting I'm missing?
If more code needs to be provided, please let me know which parts of my application would be required.
EDIT: This is my webpack.config.js:
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const APP_PATH = path.resolve(__dirname, 'src');
module.exports = {
entry: APP_PATH,
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, '/dist'),
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json']
},
module: {
rules: [
{ test: /\.(ts|js)x?$/, loader: 'babel-loader', exclude: /node_modules/ },
{ test:/\.(s*)css$/, use:['style-loader','css-loader', 'sass-loader'] },
],
},
devServer: {
historyApiFallback: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
secure: false
}
}
},
plugins: [
new HtmlWebpackPlugin({ inject: true, template: path.join(APP_PATH, 'index.html') }),
new ForkTsCheckerWebpackPlugin(),
]`enter code here`
};
That the browser is trying to load your bundle.js from http://localhost:8081/courses/1/assignmentsets/bundle.js indicates that the bundle.js is written as bundle.js instead of /bundle.js in the src attribute of the script tag in your index.html file.
To fix this you could add a publicPath to your Webpack config.
module.exports = {
entry: APP_PATH,
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, '/dist'),
publicPath: '/'
},
// ...
}
Looks like you have your script in the site like:
<script src="bundle.js"></script>
Edit it to be (/ before filename):
<script src="/bundle.js"></script>
i'm having 2 routes in my react-redux app:
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<div>
<Route path = '/a' component = {ContainerComponent} />
<Route path = '/b' component = {ContainerComponent} />
</div>
</BrowserRouter>
</Provider>,
document.getElementById('app'));
On http://localhost:8080/a i can see my ContainerComponent.
On http://localhost:8080/b i can also see my ContainerComponent.
But on http://localhost:8080/a/b i'm getting 404-error, although i expect to see two ContainerComponents at the same time, like i saw in many tutorials!
My webpack config:
var HTMLWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
var HTMLWebpackPluginConfig = new HTMLWebpackPlugin({
template: __dirname + '/app/index.html',
filename: 'index.html',
inject: 'body'
});
module.exports = {
entry: __dirname + '/app/index.js',
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}, {
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader?modules,localIdentName="[name]-[local]-[hash:base64:6]"'
})
}]
},
output: {
filename: 'transformed.js',
path: __dirname + '/build'
},
devServer: {
historyApiFallback: true
},
plugins: [
HTMLWebpackPluginConfig,
new ExtractTextPlugin('styles.css')
]
};
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<div>
<Route path = '/a' component = {ContainerComponent} />
<Route path = '/b' component = {ContainerComponent} />
<Route path = '/a/b' component = {ContainerComponent} />
</div>
</BrowserRouter>
</Provider>,
document.getElementById('app'));
You need to define the third route also. Because of the functioning of the path matching, while not using the exact param, the BrowserRouter will match as many routes as defined whose path matches the URL browsed. In this case, with URL /a, you will get two components rendered. But if you browse URL /a/b, the only route that will match the path is the third one. Because you hadn't defined in your code you get a proper 404 error.
Ok, finally, solved the problem by modifying webpack.config.js. Replaced this:
output: {
filename: 'transformed.js',
path: __dirname + '/build',
},
with this:
output: {
filename: 'transformed.js',
path: __dirname + '/build',
publicPath: '/'
},
Hello i have a problem with react-router, my code
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={StartPage}/>
<Route path="/matches" component={MatchesPage} />
<Route path="/sector/:idparam" component={SectorsPage} />
</Route>
</Router>
</Provider>,
app);
When I call /matches everything is OK, but when i try GET /sector/15 app failed try to load http://localhost:8080/sector/client.min.js but normally will load from default path (/)
Webpack:
var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
var path = require('path');
module.exports = {
context: path.join(__dirname, "src"),
devtool: debug ? "inline-sourcemap" : null,
entry: "./js/client.js",
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['react', 'es2015', 'stage-0'],
plugins: ['react-html-attrs', 'transform-decorators-legacy', 'transform-class-properties'],
}
}
]
},
output: {
path: __dirname + "/src/",
filename: "client.min.js"
},
plugins: debug ? [] : [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({mangle: false, sourcemap: false}),
],
devServer: {
historyApiFallback: true,
contentBase: './',
hot: true
},
};
I don't think the issue is with react router, or even webpack. I think your issue is related to how you are requiring your client js file, although since you didn't include that section of code I cannot confirm.
It looks like you are including the script relative to your path
(<script src="client.min.js"></script> no leading slash) instead of an absolute path (<script src="/client.min.js"></script>).
I have a webpack, react, flux, react router setup. I have these two routes:
ReactDOM.render((
<Router history={hashHistory}>
<Route path="/" component={Photos} onEnter={someAuthCheck}>
<Route path="/login" component={Login}></Route>
</Route>
</Router>
),document.getElementById('app'));
When I write http://localhost:8080/login in the browser I get:
Cannot GET /login
rather than my login dialog
I am running on the webpack dev server. What am I not doing right?
My webpack config:
var webpack = require('webpack');
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var BUILD_DIR = path.resolve(__dirname, 'src/client/public');
var APP_DIR = path.resolve(__dirname, 'src/client/app');
var config = {
resolve: {
alias: {
jquery: "jquery/src/jquery"
}
},
entry: {
main: APP_DIR + '/index.jsx',
},
output: {
publicPath: "/src/client/public/",
path: BUILD_DIR,
filename: '[name].js'
},
module : {
loaders : [
{ test : /\.jsx?/, include : APP_DIR, loader : 'babel-loader' },
{ test: /.(woff|woff2|eot|ttf)$/, loader:"url-loader?prefix=font/&limit=5000" },
{test: /\.(scss|css)$/, loader: ExtractTextPlugin.extract('css-loader!sass-loader')}
]
},
plugins: [
new ExtractTextPlugin("[name].css"),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
}),
new HtmlWebpackPlugin({
title: 'PhotoTank',
template: 'src/client/app/html-template.ejs',
filename: '../index.html'
})
],
devServer: {
//publicPath: "/src/client/public/",
//historyApiFallBack: true,
// progress: true,
//hot: true,
//inline: true,
// https: true,
//port: 8081,
contentBase: path.resolve(__dirname, 'src/client'),
proxy: {
"/api": {
target: "http://localhost:5000",
pathRewrite: {"^/api" : ""}
}
}
},
};
module.exports = config;
Usually, if you encounter Cannot GET error, it is an issue with webpack-dev-server. From the docs:
To prepare, make sure you have a index.html file that points to your bundle. Assuming that output.filename is bundle.js:
<script src="/bundle.js"></script>
So, you will have to use webpack to generate an index.html file first, usually called npm run build if you are using some boilerplate, or you have to create a separate webpack config for production build to do so.
Alternatively, just create an empty index.html file as instructed above.
There are also some problems with your react-router configuration:
<Route path="/login" component={Login}></Route>
should be
<Route path="login" component={Login}></Route>
There shouldn't be preceding slashes in children routes.
Also, you should be using browserHistory instead of hashHistory if you want to access the page at /login.
You are using hashHistory, so instead of
http://localhost:8080/login
open this:
http://localhost:8080/#/login it will work.
From Doc:
Hash history uses the hash (#) portion of the URL, creating routes
that look like example.com/#/some/path.
Read the difference between hashHistory and browserHistory.
I am trying to set up my routers for my app, and have the basic / entry point working (seemingly). It seems when I try to start adding sub routes, it is breaking. I have a pretty straight forward set up right now. I am using react + redux and my render looks like :
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory} >
<Route path="/" component={comp1.Component}>
<Route path="test" component={comp2.Component} />
</Route>
</Router>
</Provider>,
// eslint-disable-next-line no-undef
document.getElementById('app')
);
I am running webpack dev server on localhost:8080, and it serves the first route with no problem, however when I go to localhost:8080/test, I am getting a Cannot GET /test .
Here is my webpack config:
var path = require('path');
var webpack = require('webpack');
module.exports = {
devtool: 'eval',
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./client/app.jsx'
],
output: {
path: path.join(__dirname, ''),
filename: 'bundle.js',
publicPath: '/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
],
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: "babel-loader",
include: __dirname,
query: {
presets: [ 'es2015', 'react', 'react-hmre' ]
}
}]
}
}
Unsure what I am doing wrong here, would be grateful for any help. Thanks!
React Router uses the HTML5 history API. This means that 404 responses need to serve /index.html.
The docs mention how this works. You need to add this to your module.exports object:
devServer: {
historyApiFallback: true
}
Note that this only works for the CLI, when using the Node.js API you need to add this as a second parameter:
var server = new WebpackDevServer(compiler, { historyApiFallback: true });