I've got this piece of code as a router for my React 15.0.1 universal app. React router version is 2.3.0.
import React from 'react';
import { createHistory } from 'history';
import { Router, Route, IndexRoute, Redirect, useRouterHistory } from 'react-router';
import MainLayout from './../views/layout/mainLayout.jsx';
import Home from './../views/home.jsx';
import About from './../views/about.jsx';
import Error404 from './../views/404.jsx';
const browserHistory = useRouterHistory(createHistory)({
basename: '/example-url'
});
module.exports = (
<Router history={browserHistory}>
<Route path='/' component={MainLayout}>
<IndexRoute component={Home} />
<Route path='about/' component={About} />
<Route path='*' component={Error404} />
</Route>
</Router>
The problem is, that I'm getting the following error:
Invariant Violation: Browser history needs a DOM
It seems that this solution doesn't work for my server side routing, as I'm trying to set a basename for my routes.
If I forget about the basename, then everything is fine but my application doesn't behave like a spa (I believe that routes on client side are not found).
Here is some of my server side entry point (app.js):
import routes from './client/routers/routes.jsx';
const engine = ReactEngine.server.create({
routes: routes,
routesFilePath: path.join(__dirname, 'client/routers/routes.jsx'),
});
app.engine('.jsx', engine);
app.set('views', __dirname + '/client/views');
app.set('view engine', 'jsx');
app.set('view', ReactEngine.expressView);
app.get('*', function(req, res) {
res.render(req.url, { metadata: res.data.metadata,baseUrl: res.data.baseUrl, url: res.data.url });
});
Could anybody help please? I have read through the docs and supposedly this is the right way to do it and many people seem to support the solution but I'm stuck here.
Thank you very much
what's your history version?
if your react-router version is v2/v3, history version should be v3, v4 is for react-router v4
Related
first of all i'd like to say that i'm a new developer of React and NodeJS.
I want use this technologies:
- React as a client
- NodeJS as a server
- Webpack for build my files.
My project structure is the follow:
my-application/
webpack.server.js
webpack.client.js
server.js
client/client.js
client/app.js
client/components/header.js
client/components/mainLayout.js
client/components/footer.js
The header and footer files are not important so i'm not writing here.
The important file are the following:
mainLayout.js
import React, { Component } from 'react';
// import component
import Header from './header';
import Footer from './footer';
class MainLayout extends React.Component {
constructor (props) {
super(props)
}
render() {
return (
<div className="App">
<Header />
{this.props.children}
<Footer />
</div>
);
}
}
export default MainLayout;
app.js
import React, { Fragment } from 'react';
import { Route, Switch } from 'react-router-dom';
import MainLayout from './components/mainLayout'
const AppComponent = () =>
<Switch>
<Route path="/" exact render={props => (
<MainLayout>
<h1>Hello World</h1>
</MainLayout>
)} />
</Switch>
;
export default AppComponent;
client.js
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import AppComponent from './app';
ReactDOM.hydrate(
<BrowserRouter>
<AppComponent />
</BrowserRouter>,
document.querySelector('#root')
);
server.js
import express from 'express';
import React from 'react';
import AppComponent from './client/app'
var app = express();
const PORT = 3000;
app.use("/", express.static("build/public"));
app.get("/", (req, res) => {
res.send(<AppComponent />)
});
app.listen(PORT, () => console.log('Now browse to localhost:3000'));
Run my project:
npm run build
npm run dev
but when i'm going at http://localhost:3000 my page response is
{"key":null,"ref":null,"props":{},"_owner":null,"_store":{}}.
I don't understand why i have the error, something probably escapes me but i don't what.
Can you help me please?
Thanks you in advance,
AS
You are running both front and back end on the same port. Go to package.json for your react app and replace your start script with the following script:
"scripts": {
"start": "set PORT=3007 && react-scripts start",
// the rest of your scripts
}
This will be the first step for resolving your issue. If you keep getting errors after that, let us know what are they.
I ran the application again using npm.
In the command I entered: npm start in the application folder after this it worked again.
In App.js, add Routes in the import. Use element instead of component.
For Example:
import {BrowserRouter, Routes, Route, Link, Switch} from 'react-router-dom'
<Navbar />
<BrowserRouter>
<Routes>
<Route path="/" exact element = {</>}/>
<Route path="/" exact element = {</>}/>
</Routes>
</BrowserRouter>
This can be from the browser you are using.
Copy the local host address on your address bar and paste on a different browser, Chrome recommended.
This is based on personal experience. It kept rolling on Opera browser until I opened it on Chrome.
I am attempting to serve a react app from the public folder of my rails app. I am building the js file and putting it in the public folder. When I go to the root of the app, I can see that the js and my index.html page have loaded. However, when I try to go to page, like /landing, I get a 404, route not found from Rails. I can't figure out why the react router is not kicking in. This all works on dev where I am serving the react app with a second server, I only get this issue in production. Any suggestions?
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.scss';
ReactDOM.render(<App />, document.getElementById('root'));
App.js
import React from 'react';
import Auth from './util/auth';
import { Redirect, BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import MyAccount from './components/my_account';
import MyListings from './components/my_listings';
import LoginPage from './components/login_page';
import LandingPage from './components/landing_page';
import RegistrationForm from './components/registration_form';
import PasswordResetForm from './components/password_reset_form';
import RequestPasswordResetForm from './components/request_password_reset_form';
import {FlashMessages} from './components/flash_messages';
import $ from 'jquery';
import popper from 'popper.js';
import './stylesheets/App.css';
window.Popper = popper;
window.jQuery = $;
window.$ = $;
global.jQuery = $;
require('bootstrap');
const App = appProps => (
<div>
<div id="flash-messages">
<FlashMessages />
</div>
<Router>
<div className="App">
<Switch>
<Route exact name="index" path="/landing" component={LandingPage} />
<Route exact name="login" path="/login" component={LoginPage} />
<Route exact name="register" path="/register" component={RegistrationForm} />
<Route exact name="reset_password" path="/reset_password" component={PasswordResetForm} />
<Route exact name="reset_password_request" path="/reset_password_request" component={RequestPasswordResetForm} />
<PrivateRoute path="/my_account" component={MyAccount}/>
<PrivateRoute path="/my_listings" component={MyListings}/>
</Switch>
</div>
</Router>
</div>
);
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
Auth.isAuthenticated() ? (
<Component {...props}/>
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}}/>
)
)}/>
)
export default App;
A typical gotcha with React Router is that you need to return the same index.html page for all routes - be it /, /landing-page/ or /a/really/deep/route/.
You typically solve that by adding a catch-all route. I don't know rails all that well, but I think this answer might help you out.
The problem is that all routes handled first by rails and he redirects you to the page where your react routers are. And you have only one HTML page that contains your react.js code.
When you go to /login or any other page you get err 404 because you don't have a route in rails to handle it.
You need to add rails routes for all your pages and redirect them to the same index page
Or do a catch all routes to the same index page
There's some documentation for configuring your server. Basically you always need to return index.html with a 200 status code.
https://github.com/ReactTraining/react-router/blob/v3/docs/guides/Histories.md#configuring-your-server
My React router works fine when navigating to '/Article/1/' via a link, though when I refresh my browser, it no longer detects my Article component.
Root.js
import React from 'react';
import { BrowserRouter as Router, Route, Link, browserHistory } from 'react-router-dom';
import Home from '../page/Home/Index';
import Article from '../page/Article/Index';
const Root = () => {
return (
<Router history={browserHistory}>
<div>
<ul>
<li><Link to={'/'}>Home</Link></li>
<li><Link to={'/About'}>About</Link></li>
</ul>
<hr />
<Route exact path={'/'} component={Home} />
<Route path={'/Article/:id'} component={Article} />
</div>
</Router>
);
};
export default Root;
Server.js
var express = require('express');
var app = express();
app.use('/', express.static(__dirname + '/dist'));
app.listen(9000, function() {
console.log('listening on 9000.')
});
I read online that it may be related to me not having a wildcard in my server.js - could anyone point me in the right direction? thank you in advance!
EDIT - this is what I tried (still not rendering):
app.use(express.static(__dirname + '/dist'))
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname + '/dist/index.html'));
});
I read online that it may be related to me not having a wildcard in my server.js
Yep, that'll be it. The goal here is to have your client handle the routing and not the server, for that to work you need the server to return the client app regardless of what URL it gets.
Simple fix - change / to /* on your server route.
Steps to reproduce
client.js (entry file)
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { Router, browserHistory } from 'react-router';
import reduxThunk from 'redux-thunk';
import reducers from './reducers';
import routes from './routes.js';
const storeWithMiddleware = applyMiddleware(reduxThunk)(createStore);
const store = storeWithMiddleware(reducers);
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory} routes={routes} />
</Provider>, document.getElementById('app')
);
routes.js (ver 1)
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './components/bases/app.js';
import Login from './components/app/authentication/login.js';
export default(
<Route path="/" component={App}>
<Route path="signup" component={Login}/>
</Route>
)
routes.js (ver 2)
let routes = {
path: '/',
component: App,
childRoutes: [
{ path: 'signup', component: Login }
]
}
export default routes;
Expected Behavior
Expect to have /signup route avail.
Actual Behavior
react-router cannot find the route /signup but can find /
Having a look at the chrome dev-tools source-tab, this is what I find:
When looking at "/"
sources
--------
dist/prod
| bundle.js
index.html
When looking at "/signup"
sources
--------
signup
If you changed to hashHistory and it worked it probably be your backend which serves the html...
Since hashHistory works like this:
example.com/#/signup
The browser doesn't understand as a new GET, if you use browseHistory, this:
example.com/signup
Makes the browser request for index.html again but on path /signup ... but the webpack dev server probably don't understand..
Try adding historyApiFallback: true to webpack config
LIke this
https://github.com/amacneil/react-router-webpack-history-example
The giveaway is the files that are being served when you are looking at the sources. When you are trying to load the /signup page, your browser is trying to load a signup page.
When you use browserHistory, you need to serve your index.html (and any scripts included in it) for all possible routes. This means that you need to have a server which accepts all possible routes and responds accordingly.
For example, if you are running a node server using express, you would need to have a wildcard route handler:
// define static handler first for .js, etc.
app.use(express.static(path.join(__dirname, 'public')));
// all other routes should server your index.html file
app.get("/", handleRender);
app.get("*", handleRender);
function handleRender(req, res){
res.sendFile(__dirname + '/index.html');
}
I have installed React Router and am using browserHistory, but when I manually type the URL, I get Cannot GET /:path.
Any ideas why this might be?
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import {Router, browserHistory} from 'react-router';
import routes from './config/routes';
ReactDOM.render(<Router history={browserHistory} routes={routes} />, document.getElementById('app'));
routes.js
import React from 'react';
import {Route, IndexRoute} from 'react-router';
import App from '../components/app';
import Dashboard from '../components/dashboard';
import Expenditure from '../components/expenditure';
import Income from '../components/income';
import Transactions from '../components/transactions';
export default(
<Route path='/' component={App}>
<IndexRoute component={Dashboard} />
<Route path='expenditure' component={Expenditure} />
<Route path='income' component={Income} />
<Route path='transactions' component={Transactions} />
</Route>
);
Clicking on the nav displays the content perfectly with the correct route.
Am using webpack to compile with webpack-dev-server.
Thanks :)
You have to redirect all non-file requests to index.html, which is required for browserHistory to work.
In webpack-dev-server there is an option historyApiFallback, which you have to set to true to make it happen.
See webpack documentation for more information
new WebpackDevServer(compiler, {
historyApiFallback: true
// ...
})
I meet the same question today and cann't find answer in stackoverflow.
let path in webpack.config.js: output.publicPath be equal to devServer.historyApiFallback.index and point out html file route。my webpack-dev-server version is 1.10.1 and work well. http://webpack.github.io/docs/webpack-dev-server.html#the-historyapifallback-option doesn't work, you must point out html file route.
for example:
module.exports = {
entry: "./src/app/index.js",
output: {
path: path.resolve(__dirname, 'build'),
publicPath: 'build',
filename: 'bundle-main.js'
},
devServer: {
historyApiFallback:{
index:'build/index.html'
},
},
//其他的配置省略
};
historyApiFallback.index indicate that when url path not match a true file,webpack-dev-server use the file config in historyApiFallback.index to show in browser rather than 404 page. then all things about your route change let your js using react-router do it.