-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[v6] Add <HistoryRouter> for standalone history objects #7586
Conversation
I think rather than overriding the internal history of BrowserRouter, it would make more sense to create a HistoryRouter in the react-router package that takes a |
Yes, i agree! I have modified the PR with HistoryRouter. Let's have a look! |
Sorry for the request, i havent seen that you have already approved. |
Hi Tim, can we see this pr merged on nest beta? |
Michael or Ryan would need to approve this. They have a vision for v6, so they need to provide input as to whether this fits that or not. |
Would be great if this landed on v6, as useNavigate/useBlocker falls short in some cases where the history object can solve it directly (like navigating in some places where the hook is not available, or adding more history listeners to have more control in certain actions) |
We had an issue with Authentication. Our Auth provider needed to use The solution was to use this HistoryRouter, have the Auth Provider use I think this is needed. |
Same case as @eturino. Would love to see it being picked up. Really appreciate the PR and v6. |
@salvoravida While using this without being merged, I had some use cases were I needed to alter newState or trigger actions directly at the listener (since secondary listeners were run later), so I expanded your component to also receive a reducer and historyListener as prop: /**
* A <Router> for use in web browsers with custom history. Provides the cleanest URLs.
*/
export function HistoryRouter({
children,
history,
reducer = (_: Update, action: Update) => action,
historyListener
}: HistoryRouterProps) {
let [state, dispatch] = React.useReducer(reducer, {
action: history.action,
location: history.location
});
React.useLayoutEffect(
() =>
history.listen(
historyListener !== undefined
? action => historyListener(action, dispatch)
: dispatch
),
[history, historyListener]
);
return (
<Router
children={children}
action={state.action}
location={state.location}
navigator={history}
/>
);
}
export interface HistoryRouterProps {
children?: React.ReactNode;
history: BrowserHistory;
reducer?: (state: Update, action: Update) => Update;
historyListener?: (action: Update, dispatch: React.Dispatch<Update>) => void;
}
if (__DEV__) {
HistoryRouter.displayName = 'HistoryRouter';
HistoryRouter.propTypes = {
children: PropTypes.node,
history: PropTypes.object,
reducer: PropTypes.func,
historyListener: PropTypes.func
};
} Feel free to merge it in your PR if you find it useful! |
I have the same request, but our use case is different. We're using single-spa to build micro-frontends. We want to use the same It would be great to see |
Hi Tim, i have updated the pr, without proptypes as others Router. |
if you use stack with redux-saga you could run into this screnario: |
https://github.com/salvoravida/redux-first-history you can use put(push) from redux-first-history (that works already for rr5.) we also need this PR to support rr6 |
@timdorr could we see this pr merged next beta? |
Whats the status of this? :) |
Thanks for the PR, but you can already do this in v6 with a regular let history = createHistory();
<Router location={history.location} navigator={history}>
...
</Router> A |
Meanwhile this PR is merged, I have added it on redux-first-history@5.0.0-beta.2 import { HistoryRouter as Router } from "redux-first-history/rr6";
import { Route, Routes, Navigate } from "react-router-dom";
//...
import { store, history } from "./store";
//...
<Router history={history}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/" element={<Home />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</Router> demo rr6 here: https://codesandbox.io/s/redux-first-history-demo-rr6-uccuw |
That’s right. If you’re using your own custom history and using some external location store (like Redux) you will need to setup the listener yourself somewhere (probably when you initialize your store) and pass the location in as it changes.
The whole point of this approach is that you want to store the location yourself, so you need to setup the listener anyway.
Edit: if you're using Redux to keep track of location state (or some other external state manager), there are lots of different ways you can do it depending on your application setup, so we give you the tools to put it together however you want.
This is pseudo-code but the general pattern for using `<Router>` with an external state mechanism (like Redux) looks something like this:
```js
let store = createStore();
let history = createHistory();
history.listen(({ location }) => {
store.update(location);
});
function MyApp() {
return (
<Router location={history.location} navigator={history}>
...
</Router>
);
}
export default connect(MyApp, store);
```
Our contract with external state managers is the `<Router>` interface. Just plug in the current state (`action` and `location`) as a prop and the `navigator` (the "state changer") and you can use whatever external storage you like.
The main caveat here is that if you're using external storage with v6 you may run into some race condition issues when we release some of the features we have planned around React Suspense. Your storage layer may not be aware of things like pending navigation and data loading/mutations, so you may run into some issues there if you're not careful.
|
I'm also puzzled why this won't be merged, it's a requested feature and makes users write less code for common use cases. |
@h3rmanj @ejose19 import { HistoryRouter } from "redux-first-history/rr6"; or save to HistoryRouter.js on your project: import React from 'react';
import { Router } from 'react-router';
export function HistoryRouter({ children, history }) {
const [state, setState] = React.useState({
action: history.action,
location: history.location,
});
React.useLayoutEffect(() => history.listen(setState), [history]);
return React.createElement(Router, Object.assign({ children, navigator: history }, state));
} |
I think I may have been too focused on the Redux use case when I commented earlier. |
Any movement on the @salvoravida I assume it should still work with the latest |
yes you can test https://github.com/salvoravida/redux-first-history/releases/tag/v5.0.0-beta.2 |
This PR needs an update for latest release (6.0.2) as now it's issuing this error:
|
4298778
to
cc31f39
Compare
done! |
What is the timeline for getting this in a release? |
Based on Michael's comments earlier, let's get this merged in for the next release. |
Co-authored-by: Tim Dorr <git@timdorr.com>
I was dragging my feet on doing anything about this because history is a regular dep of the router in v6, not a peer dep. So anyone who uses the Sorry I wasn’t more communicative about this. I sympathize with the OP and everyone who needs this feature, but we have definitely created another potential bundling issue by merging this work. |
Good point, and thanks for working to give us an option in spite of that! I agree it feels a little dirty since a lot of v6 tries to abstract away history. I'm definitely open to alternatives, so long as it solves the problem summarized in #8342. For what it's worth, my project and hopefully the majority of React projects out there only included history to provide it for react-router, so I was able to just remove it after the upgrade making that a non-issue. |
I understand your concerns, but honestly, I do not see any potential bugs allowing just to use an external history object:
That said, great work with the new react-router v6! Regards! |
Just to add to this, for Micro-frontends, it is also useful to pass the history object to other apps on same page. See https://blog.bitsrc.io/how-to-develop-microfrontends-using-react-step-by-step-guide-47ebb479cacd for React-router v5 implementation. For v6, we would need to clone the BrowserRouter Object to be able to pass a history object. |
#7585
Hi guys, great work with v6 release!
Just a little note:
It would be nice to add support for a already created history object on <BrowserRouter
and skip creation if it is passed as prop.
We need it to support redux-first-history integration.