I\'m using React-router and it works fine while I\'m clicking on link buttons, but when I refresh my webpage it does not load what I want.
For instance, I am in
In case, anyone is here looking for solution on React JS SPA with Laravel.
The accepted answer is the best explanation of why such problems happen. As already explained you have to configure both client side and server side.
In your blade template, include the js bundled file, make sure to use URL facade
like this
<script src="{{ URL::to('js/user/spa.js') }}"></script>
In your routes, make sure add this to the main endpoint where the blade template is. For example,
Route::get('/setting-alerts', function () {
return view('user.set-alerts');
});
The above is the main endpoint for the blade template. Now add an optional route too,
Route::get('/setting-alerts/{spa?}', function () {
return view('user.set-alerts');
});
The problem that happens is that first the blade template is loaded, then the react router. So, when you're loading '/setting-alerts'
, it loads the html and the js. But when you load '/setting-alerts/about'
, it first loads on the server side. Since on the server side, there is nothing on this location, it returns not found. When you have that optional router, it loads that same page and react router is also loaded, then react loader decides which component to show.
Hope this helps.
I had this same problem and this solution worked for us..
Background:
We are hosting multiple apps on the same server. When we would refresh the server would not understand where to look for our index in the dist folder for that particular app. The above link will take you to what worked for us... Hope this helps, as we have spent quite a hours on figuring out a solution for our needs.
We are using:
package.json
"dependencies": {
"babel-polyfill": "^6.23.0",
"ejs": "^2.5.6",
"express": "^4.15.2",
"prop-types": "^15.5.6",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-redux": "^5.0.4",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.8",
"redux": "^3.6.0",
"redux-persist": "^4.6.0",
"redux-thunk": "^2.2.0",
"webpack": "^2.4.1"
}
my webpack.config.js
webpack.config.js
/* eslint-disable */
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const babelPolyfill = require('babel-polyfill');
const HTMLWebpackPluginConfig = new HtmlWebpackPlugin({
template: __dirname + '/app/views/index.html',
filename: 'index.html',
inject: 'body'
});
module.exports = {
entry: [
'babel-polyfill', './app/index.js'
],
output: {
path: __dirname + '/dist/your_app_name_here',
filename: 'index_bundle.js'
},
module: {
rules: [{
test: /\.js$/,
loader: 'babel-loader',
query : {
presets : ["env", "react", "stage-1"]
},
exclude: /node_modules/
}]
},
plugins: [HTMLWebpackPluginConfig]
}
my index.js
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import Routes from './Routes'
import { Provider } from 'react-redux'
import { createHistory } from 'history'
import { useRouterHistory } from 'react-router'
import configureStore from './store/configureStore'
import { syncHistoryWithStore } from 'react-router-redux'
import { persistStore } from 'redux-persist'
const store = configureStore();
const browserHistory = useRouterHistory(createHistory) ({
basename: '/your_app_name_here'
})
const history = syncHistoryWithStore(browserHistory, store)
persistStore(store, {blacklist: ['routing']}, () => {
console.log('rehydration complete')
})
// persistStore(store).purge()
ReactDOM.render(
<Provider store={store}>
<div>
<Routes history={history} />
</div>
</Provider>,
document.getElementById('mount')
)
my app.js
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/dist'));
// app.use(express.static(__dirname + '/app/assets'));
app.set('views', __dirname + '/dist/your_app_name_here');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.get('/*', function (req, res) {
res.render('index');
});
app.listen(8081, function () {
console.log('MD listening on port 8081!');
});
Add this to webpack.config.js
:
devServer: {
historyApiFallback: true
}
Fixing the "cannot GET /URL" error on refresh or on calling the URL directly.
Configure your webpack.config.js to expect the given link the routes like this.
module.exports = {
entry: './app/index.js',
output: {
path: path.join(__dirname, '/bundle'),
filename: 'index_bundle.js',
publicPath: '/'
},
If you do have a fallback to your index.html, make sure that in your index.html file you have this:
<script>
System.config({ baseURL: '/' });
</script>
This may differ from project to project.
I'm not using server side rendering yet but I hit the same problem as the OP where Link seemed to work fine most of the time but failed when I had a parameter. I'll document my solution here to see if it helps anyone.
My main jsx contains this:
<Route onEnter={requireLogin} path="detail/:id" component={ModelDetail} />
This works fine for the first matching link but when the :id changes in <Link>
expressions nested on that model's detail page, the url changes in the browser bar but the content of the page did not initially change to reflect the linked model.
The trouble was that I had used the props.params.id
to set the model in componentDidMount
. The component is just mounted once so this means that the first model is the one that sticks on the page and the subsequent Links change the props but leave the page looking unchanged.
Setting the model in the component state in both componentDidMount
and in componentWillReceiveProps
(where it is based on the next props) solves the problem and the page content changes to reflect the desired model.