accessing resources within nested react-routes using webpack file-loader - reactjs

I have recently switched my routing using react-router from hashHistory to browserHistory. There is a problem though. Everytime I access a nested route and try and refresh the page, public resources such as my logo can't be found as it uses the first part of the nested route as the root for the resource.
In my index.js I require the image as follows:
require('./images/logo-icon.png');
I am using a logo in my Navigation Bar component as follows:
<Link to="/" className="gc-padding-none">
<img alt="Get Cooked" className="gc-logo-default" src="images/logo-icon.png" />
</Link>
and here are my routes:
<Route path="/" component={NavigationBar}>
<IndexRoute component={Home} />
<Route path="/chefs" component={ProfileList} />
<Route exact path="register" component={Register} redirect />
<Route path="chef">
<Route path="register" component={RegisterChef} />
</Route>
</Route>
and here is my webpack config:
var HtmlWebpackPlugin = require('html-webpack-plugin');
var HTMLWebpackPluginConfig = new HtmlWebpackPlugin({
template: __dirname + '/client/index.html',
filename: 'index.html',
inject: 'body'
});
module.exports = {
entry: [
'./client/index.js'
],
output: {
path: __dirname + '/dist',
filename: 'index_bundle.js',
publicPath: '/'
},
devServer: {
historyApiFallback: true,
inline: true,
contentBase: './client',
port: 8080,
proxy: { '/api/**': { target: 'http://localhost:3000', secure: false } }
},
module: {
loaders: [
{test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', query: {presets: ['es2015','react'], plugins: ['transform-es2015-destructuring', 'transform-object-rest-spread']}},
{test: /\.jpe?g$|\.gif$|\.svg$|\.png$/i, loader: 'file-loader?name=/images/[name].[ext]'},
{test: /\.css$/, loaders: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader','resolve-url-loader']},
{test: /\.scss$/, loaders: ['style-loader', 'css-loader','postcss-loader', 'sass-loader','resolve-url-loader']}
]
},
plugins: [HTMLWebpackPluginConfig]
};
Every time I go to '/' or '/chefs' everything loads fine, however, when I access a nested route such as '/chef/register' and refresh my browser the logo cannot be found in the Navigation Bar component.
The error below is presented:
logo-icon.png:1 GET http://localhost:8080/chef/images/logo-icon.png 404 (Not Found)
As you can see the logo is trying to be fetched from a location which includes the first part of my route '/chef'.
I have tried removing the publicPath configuration in my webpack, however, that affects the rendering of nested components on refresh.
Any ideas on why this is happening?

To explain elmeister's comment,
images/logo-icon.png is a relative path making it dependent upon the current path.
So the request for images/logo-icon.png from url http://localhost:8080/chef/ results in a request for http://localhost:8080/chef/images/logo-icon.png.
/images/logo-icon.png is an absolute path ensuring the path is always based upon the root of the site, no matter what the current path might be.
So a request for /images/logo-icon.png from url http://localhost:8080/chef/ will result in a request for http://localhost:8080/images/logo-icon.png.

Related

subdirs in url breaks react route with custom webpack setup

I have a simple UI where I display a list of objects and then a form to add one.
The default page shows the list of objects with a menu to add a new one. When I click on the new link, I get a 404
When I click on New Company I get a blank screen and a 404 because it cannot find the main.js
Wondering what the hell is main.js I was looking through my code and see I have it defined in my webpack config;
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "public"),
filename: "main.js"
},
target: "node",
devServer: {
port: "9000",
contentBase: ["./public"],
open: true,
historyApiFallback: true
},
resolve: {
extensions: [".js", ".jsx", ".json"]
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
include: /src/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
'css-loader',
'postcss-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "styles.css"
}),
new HtmlWebpackPlugin({
template: "public/index.html" //source html
}),
]
}
I thought I had my webpack setup correctly but I think I must be missing something?
Running more tests I added a simple about page that just displayed a heading and had it at /about
const CompanyBox = () => (
<>
<Menu/>
<Switch>
<Route exact path="/">
<CompanyList/>
</Route>
<Route exact path="/companies/new">
<CompanyForm/>
</Route>
<Route path="/companies/:companyId">
<CompanyDetails/>
</Route>
<RolesRoute path="/secret" roles={['admin']}>
<SecretCompanies/>
</RolesRoute>
<Route path="/about">
<About />
</Route>
<Route path="*">
<NoMatch/>
</Route>
</Switch>
</>
)
function About() {
return (
<div>
<h2>About</h2>
</div>
);
}
I also added it to the links in the menu. When I go to the URL directly or via the link it both works
But when I change the url to something like /about/new, I get the error on the console that it can't find the main.js
Not sure why the routes doesn't work when I go 2 levels deep in the url directories
Found the answer here.
I knew it was a webpack config issue, but turns out I had the path to the html file set relatively and not absolute

React + Webpack: can't redirect all my routes to index.html

I'm building a React application without using create-react-app, but using webpack to build it and webpack-dev-server to serve it.
My directory structure is:
myApp
|
|---docs/
| |
| |---dist/
| | |
| | |---bundle.js
| |---index.html
|---src/
|---package.json
|---package-lock.json
|---README.md
|---webpack.config.js
and my webpack.config.js is:
module.exports = {
entry: __dirname + '/src/index.js',
output: {
filename: 'bundle.js',
path: __dirname + '/docs/dist'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: [/.css$|.scss$/],
use: [
'style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.(jpg|jpeg|png|jpg|gif)$/,
loader: 'file-loader?limit=10000&name=../assets/images/[name].[ext]'
},
{
test: /\.(woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000&name=../assets/fonts/[name].[ext]'
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
devServer: {
contentBase: './docs',
compress: true,
port: 9000,
historyApiFallback: true
}
};
The Webpack documentation about historyApiFallback says this configuration historyApiFallback: true will redirect all 404 responses to index.html.
It happens that I am using react-router-dom and I have the following routes:
<BrowserRouter>
<Switch>
<Route exact path="/" component={Homepage} />
<Route path="/about" component={About} />
<Route path="/article/:id/show" component={Article} />
<Route path="/donate" component={Donate} />
</Switch>
</BrowserRouter>
Everything is fine with /, /about and /donate, but when I try the route /article/<something>/show I still get a 404 error message, as the image below shows.
It says it can't find the bundle.jsfile. But then, it is not redirecting my route to index.html in the first place. If it were doing so, it would find the bundle.jsfile, as it does in the other routes.
How may I do this work?
EDIT
Setting publicPath to /, as suggested, won't solve the problem. First of all, / is not my asset's path.
Setting it /docs/dist, my real public asset's path, work to some extent. But the solution comes only if you use an absolute path when loading the resources.
You need to specify the publicPath in the output object like so:
output: {
filename: 'bundle.js',
path: __dirname + '/docs/dist',
publicPath: '/'
},
You can read the doc for more insight.

React Route does not work with nested URLs

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>

React router cannot get my entry js

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>).

React router + webpack, sub routes not working

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 });

Resources