using React Router with browserHisory - reactjs

I'm developing an app which consists in React + Redux.
We just turned it into a SPA with React Router.
While searching the web, I see everybody uses brwoserHistory on their router.
<Router history={createBrowserHistory()}>
....
</Router>
I understand the concept of history, and see the advantages of controlling the history stack.
Currently, I'm not using it and it works fine, I'm new to React Router and I want to know if i need it.

A history object to use for navigation.
import createBrowserHistory from 'history/createBrowserHistory'
const customHistory = createBrowserHistory()
<Router history={customHistory}/>
more info docs

Related

react-router-domv5 + history push does not render component [duplicate]

Is it possible to create a global history file to manage the createBrowserHistory() on react-router-dom v5?
I know the V5 has the useHistory() as a way to get the history. But is it possible to retrieve the history from anywhere, like for cases where I am not using a function component?
On V4 I could create a file history.js:
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
It works on V4
https://codesandbox.io/s/react-router-v4-nfwr0
It doesn't work on V5 - It updates the URL but redirects to not-found
https://codesandbox.io/s/react-router-v5-not-working-jlrep
As the doc says you should use the v4 of history to work on react-router v5.
https://github.com/ReactTraining/history
Documentation for version 4 can be found on the v4 branch. Version 4 is used in React Router versions 4 and 5.
I solved this by doing this.See what i done
Create a file like before
This is the code
import {createBrowserHistory} from 'history';
import store from 'store';
export default createBrowserHistory({
basename: store ? store.getState().productionBaseUrl : ''
});
The reason why i import Redux is the project does not pulish on nginx root folder.So i should add a basename.If you do not neet it,you can remove it.
Then you can use it in your own coponnet. How to use? Let me show your the code.
// if my history in src/router/
import history from 'router/history';
history.push(`${your router address}`)
Attention
The history's push method can pass an object like the origin props.But it's refresh when in the child router always. So pass a string when use it.

Removing # from HashRouter

I'm using react-router-dom for my routing and, since I'm also using GitHub Pages, I need to use HashRouter in my Router.jsx like so
import React from 'react';
import { HashRouter as Router, Route, Switch } from 'react-router-dom';
import Home from './Home';
import Customer from './Customer';
const MyRouter = () => (
<Router>
<Switch>
<Route path="/customer" component={Customer} />
<Route path="/" component={Home} />
</Switch>
</Router>
);
export default MyRouter;
In my Home.jsx component I have defined my propTypes like so
Homepage.propTypes = {
history: PropTypes.shape({ // set by react-router
push: PropTypes.func.isRequired,
}).isRequired,
};
My problem is that everytime I get a # in my URL and I would like to know why is it there all the time and why does my localhost without # redirect me to the same URL but with #(like if I go to http://localhost:4000/myApp/ it will redirect me to http://localhost:4000/myApp/#/). I would like to get rif of it for tracking purposes. I've tried using BrowserRouter but it won't work, as well as the history parameter for the Router like history={createHashHistory({ queryKey: false })} or history={browserHistory}.
Thank you very much (and sorry for my English)
Due to the front end nature of your client side React application, routing is a bit of a hack. The functionality of the two main router options are as follows :
HashRouter uses a hash symbol in the URL, which has the effect of all subsequent URL path content being ignored in the server request (ie you send "www.mywebsite.com/#/person/john" the server gets "www.mywebsite.com". As a result the server will return the pre # URL response, and then the post # path will be handled by parsed by your client side react application.
BrowserRouter will not append the # symbol to your URL, however will create issues when you try to link to a page or reload a page. If the explicit route exists in your client react app, but not on your server, reloading and linking(anything that hits the server directly) will return 404 not found errors.
The solution to this problem can be seen on this extensive post : https://stackoverflow.com/a/36623117/2249933
The main principle is that you match your client side routes, with routes on your server, that way allowing clean url's, but without the limitations of browser router on it's own.
I faced this issue today. I was trying to learn dynamic routing and I used hashRouter first. After a while, I want to get rid of hash sign and then learned that all purpose of the hashRouter is completely different than what I am trying to achieve.
But converting current hashRouter was easy if you understand your current structure.
I am using webpack and testing them on webpack-dev-server, I have lots of nested routes and this method is working for me.
import { createBrowserHistory } from "history";
import { Route, Switch, Redirect, BrowserRouter } from "react-router-dom";
const hist = createBrowserHistory();
<BrowserRouter history={hist}>
<Switch>
<Route path="/" component={ComponentName} />
</Switch>
</BrowserRouter>
Note: Addition to that for webpack there are some configs to add like follows;
In webpack.config.js
output: {
...
publicPatch: "/",
...
}
devServer: {
...
historyApiFallback: true,
...
}
As I understand,
publicPath resolve finding chunked code connections with giving them
predetermined path (which is an issue on dynamic routing to call
nested routed pages)
historyApiFallback is the magic in here. Nested routes don't connect to browserHistory, when you are trying to request the link since this is client-side routing, it returns blank. But if you put historyApiFallback config, dev-server returns all unknown links to index.html. That means all nested routes links are treating as unknown, but since it loads index.html to the user. User can access the pages with their determined route addresses.
HashRouter ... is navigation via everything after the # (called a hash).
https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/HashRouter.md
If you don't want that, you can try BrowserRouter - edit: I see you said BrowserRouter doesn't work. In what way?

Confusion react-router-dom and react-router-redux

I'm new to react and redux, I started out my project with the react-router-dom, now I'm looking to add Redux in the mix.
Should I keep using react-router-dom or just install react-router-redux? or both?
Does the Route component work the same? I'm a bit confused as what the use of react-router-dom would be?
Do I switch all the Link components to an a element that dispatches an action to the store?
Thanks for any help.
Generally when using Redux, it's recommended to use react-router-redux, which encapsulates react-router.
Only when you initialize your app, you should pass a couple of things from react-router to Redux, which are Router and browserHistory.
In app.js:
import { Router, browserHistory } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
const initialState = {};
const store = configureStore(initialState, browserHistory);
const history = syncHistoryWithStore(browserHistory, store);
const render = () => {
ReactDOM.render(
<Provider store={store}>
<Router
history={history}
routes={rootRoute}
/>
</Provider>,
document.getElementById('app')
);
};
You provide the Router element of react as a child of Redux's Provider element, and you also provide to the Router element the Redux wrapper for React's browserHistory.
In the rest of your project, you only need to use react-router-redux, rather than calling React directly.
react-router-dom BTW is just another layer of simplification in React 4 which encapsulates react-router, but in case your project architecture is Redux, than you need to work according to the rules of Redux and therefore only use react-router-redux in order to pass through the store in each action (except for the aforementioned initialization).
i will recommend you to use react-router-redux.
using react-router-redux you can connect your router state with your Redux Store so that you can Interact with the Router with the same API you use to interact with the rest of your app state.
Use react-router-redux.
With react-router-redux the location state is kept in your redux store as plain object. This means you can inject location information with connect as you would any other state.

React Router & Express Conflicts

I have paths that I want React Router to handle, and I also have an express API backend that I call from the React app to do some secure API calls.
/#/ - want app served here
/#/:id - unique URLs for the app, i use the ID to call certain express endpoints from React.
* - all express REST endpoints
Express app.js:
app.use(middleware);
app.use(require('webpack-hot-middleware')(compiler, {
log: console.log
}));
app.use('/api', [router_invokeBhApi]);
app.get('/#/*', function response(req, res) {
res.write(middleware.fileSystem.readFileSync(path.join(__dirname, 'dist/index.html')));
res.end();
});
app.use(express.static(path.join(__dirname, '/dist')))
My React router component:
export default (
<Route path="/" component={App}>
<IndexRoute component={HomePage} />
<Route path="/:id" component={ConsentForm} something="dope"/>
</Route>
);
So here's what's happening:
- going to localhost:8000 serves the app with the HomePage component
- going to localhost:8000/#/ also serves the app with the HomePage component
- going to localhost:8000/example gives me Cannot GET /example which means express is working
- going to localhost:8000/api gives me a test JSON object which i send from express, which is correct.
- going to localhost:8000/#/somehashcode STILL gives me the HomePage component, when it SHOULD be giving me the ConsentForm component.
I inspected the Router component using React Dev tools:
- the RouterContext component has an object called routes with a childRoutes inside the routes[0], and it has the path /:id. routes[0] also has a path / which tells me React Router loaded all the routes correctly??
So confused...
React file where I render the whole app:
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, browserHistory } from 'react-router';
import Routes from './shared/components/Routes';
import './shared/base.css';
import './shared/customInput.css';
const ROOT_ELEMENT = 'app';
ReactDOM.render((
<Router history={browserHistory}>
{Routes}
</Router>
), document.getElementById(ROOT_ELEMENT));
I finally figured it out!
In the React Router component change the :id route to:
<Route path="/redirect/:id" component={ConsentForm} something="dope"/>
In Express, app.js change the React serve to:
app.get('/redirect/*', function response(req, res) {
res.write(middleware.fileSystem.readFileSync(path.join(__dirname, 'dist/index.html')));
res.end();
});
Learnt 2 things from this:
- Using # in the route doesn't work. Not sure why.
- Explicitly state the route in the React side as well.
Quite weird, or maybe I'm going off some half-baked knowledge. Any resource to point me in the right direction would be so awesome.
Pretty sure someone else is going to run into this as they use Express and React together.
Found your problem.
You're using BrowserHistory in your configuration. This option (the recommended one at present) does not use a hash in the uri, but instead directly integrates with the browser.
Try hitting your app at localhost:8000/somehashcode and see what you get.

react router v4 programmatically change route

Cannot seem to find much quality documentation on this. React Router v4 documentation has information of the 'history' object but states that it is mutable and should not be changed. Many examples I have seen seem like 'hacks'. Any insight would be appreciated.
I had to grapple with this one for a while as a react newbie myself. While the link that #ToddChaffee provided gives you a good insight, this thread highlights that the history object is automatically passed as a prop when you have <Router/> as the top level component. That makes it much simpler than loading the history object separately and using the createBrowserHistory() method.
The following worked for me:
<Route exact path='/' render={({history}) =>
<Home infoHandler={this.handleUserInfo}
imageHandler={this.handleImage}
history={history}/>
}/>
Then inside the component you can:
this.props.history.push('/routeYouWantToGoTo');
Don't forget to typecheck the prop in you component before you export the module:
Home.propTypes = {
history: React.PropTypes.object
}
export default Home;

Resources