-
Notifications
You must be signed in to change notification settings - Fork 961
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
Where did basename
go? [v5]
#810
Comments
According to the summary on the pull request, basename support was removed in v5: |
😢 Should probably be mentioned in the "Breaking changes" section of the release. |
Maybe I've been "doing it wrong" this whole time, but my React app sits on a subpath of a larger app, and so I used I define a simple custom history object: ...which I then pass into:
I do this because I want access to So without the Is there a "better" way of handling what I need to do? I understand there is a So am I just stuck on history 4.x forever? Is there something I'm missing? Thanks! |
@bmueller-sykes In the same boat, so following along. |
@bmueller-sykes exactly like you, I think there should be ways to let us using history out of react context and there should be basename option to let us make our app on a subpath thank you for opening this issue now is there any option? what should we do? |
We also have infrastructure over createBrowserHistory and I find the removal of the API without any documentation or a way around it is really odd. Can some maintainer please provide a W/A or are we supposed to wrap and add the basename to every route? |
FWIW: It looks like React Router 6 will have a mechanism for basenames: Edit: I get that this isn't a fix for everyone. |
@StringEpsilon Hmm...I'm not sure that resolves the issue, though, right? If you want to do a |
I agree with @bmueller-sykes, for now we will not update our history, but I'm hoping we wont get stuck on v4. |
I think that history must work without react router or react , It could to be used outside of react mechanism |
I'd be happy to PR adding |
The (undocumented) removal of Looking at the new I should add: Thank-you very much for the free software and the support that goes into it, it's just that the way this change has been handled has been a bit frustrating for some of us |
Maybe I'm doing something wrong, but in react-router v5 I can't work out how to pass in my own Looks like In v5 the best I can come up with to use
Then replace my current
Has anyone else worked out a better way to do this? NOTE: initially based on other's comments I thought this history ref would not be basename aware, but upon testing it seems that it is |
@cinnabarcaracal that's a clever way of solving this issue, but it sure doesn't seem like it's the way this is "supposed" to be used. And, yeah, I should have mentioned more clearly that I use my independent ...and +1 about the whole free software thing. (-; |
in the same situation, I will be stuck in version 4 |
This also breaks Sentry integration: https://docs.sentry.io/platforms/javascript/guides/react/integrations/react-router/#react-router-v4v5 The above workaround would work, but ideally you'd want to set Sentry up before rendering anything. |
I'm also affected by this change in several projects. |
Probably doesn't cover all use cases, but I did a quick and dirty workaround for a small project I'm working on, feel free to modify if necessary. Maybe it will be of use to some, at least for an idea how to work around the issue for now. Create file import {createBrowserHistory} from "history";
function appendBaseName (basename, to, state, callback) {
if (typeof to === 'string') {
to = basename + to;
}
if (typeof to === 'object' && to.pathname) {
to.pathname = basename + to.pathname;
}
if (state !== undefined && state.pathname) {
to.pathname = basename + state.pathname;
}
return callback(to, state);
}
export default function createBrowserHistoryWithBasename(basename = '/') {
let history = createBrowserHistory();
history.basename = basename;
const push = history.push;
const replace = history.replace;
history.push = (to, state = undefined) => appendBaseName(basename, to, state, push);
history.replace = (to, state = undefined) => appendBaseName(basename, to, state, replace);
return history;
}; Put the following where you import history API import createBrowserHistoryWithBasename from "./utils/history";
let history = createBrowserHistoryWithBasename('/your/base/path'); // Replace argument with your basename
... |
if we use |
I think this should work, I don't if it was added later on |
@mukuljainx The issue is that a valid use-case is to use the |
My Solution {
"start_url": "/subfolder/build/",
}
{
"homepage": "/subfolder/build/",
} Adding in my import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
<React.StrictMode>
<BrowserRouter basename="/subfolder/build/">
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById("root")
); Advantages
My Version
Just additional infoThe compiling looks like this: PS C:\Users\ruper\Documents\Code\ReactTypescript\myapp> npm run build
> [email protected] build C:\Users\ruper\Documents\Code\ReactTypescript\myapp
> react-scripts build
Creating an optimized production build...
Compiled with warnings.
src\assets\images\index.tsx
Line 22:10: Embedded <object> elements must have alternative text by providing inner text, aria-label or aria-labelledby props jsx-a11y/alt-text
src\component\Home.tsx
Line 3:3: 'RefObject' is defined but never used @typescript-eslint/no-unused-vars
Line 4:3: 'useEffect' is defined but never used @typescript-eslint/no-unused-vars
Line 5:3: 'useRef' is defined but never used @typescript-eslint/no-unused-vars
Line 6:3: 'createRef' is defined but never used @typescript-eslint/no-unused-vars
src\component\Profile\Profile_Rupert.tsx
Line 83:17: Anchors must have content and the content must be accessible by a screen reader jsx-a11y/anchor-has-content
Search for the keywords to learn more about each warning.
To ignore, add // eslint-disable-next-line to the line before.
File sizes after gzip:
109.68 KB (+147 B) build\static\js\2.1b5b618e.chunk.js
29.2 KB build\static\css\main.048ad125.chunk.css
5.24 KB (-22 B) build\static\js\main.e7a21f6b.chunk.js
1.59 KB build\static\js\3.c64630cc.chunk.js
1.18 KB build\static\js\runtime-main.ba8aaa23.js
The project was built assuming it is hosted at /subfolder/build/.
You can control this with the homepage field in your package.json.
The build folder is ready to be deployed.
Find out more about deployment here:
https://cra.link/deployment
PS C:\Users\ruper\Documents\Code\ReactTypescript\myapp> Here is the new baseurl, extracted from compiling log: |
Following up on this. Any more-ideal fixes? We manually set the window state to navigate to page fragments and tabs. We rely on history.location.pathname to include our basename. |
@bmueller-sykes what about using |
@mukuljainx that's exactly what I've been doing |
The basename functionality was removed from this package primarily to save on size. History v5 is about 50% smaller than v4, and a lot of that code had to do with supporting the basename feature; both stripping the basename from incoming pathnames when If you're using React Router, this won't affect you because React Router v6 includes support for basenames both when matching routes and when navigating (i.e. when React Router calls out to the history library, it already knows the full pathname to use for navigation, so it doesn't rely on history's basename feature). More details are in the v5 => v6 migration guide. If you aren't using React Router, you'll have to construct the full pathnames yourself before sending them to the history library for navigation. This could be as simple as: let basename = '/my-basename';
function push(url) {
history.push(basename + url);
}
push('/some/relative/pathname'); |
@mjackson If I'm reading your reply correctly, it means there's no way to solve my issue above until RRv6 comes along (it's not out yet, right? the latest version is 5.2 on Github), is that correct? To recap, I've got a custom history object, which I define like this:
...which I then pass into:
...but I also use completely outside the context of RR (like inside
So you're saying the new approach is something like (apologies for pseudo-code):
|
@bmueller-sykes, there is a beta release of React Router v6; you can see the source code for that in the |
@bmueller-sykes We are moving away from the idea of using your own custom history object in RR v6. If you need basename support, you can use |
@pshrmn I am indeed using v4 with RRv5. (-; @mjackson will we be able to access RR's API from outside React components? e.g. from within a Thanks to you both for replying! |
Probably not, no. React Router is a React library, so it's supposed to be used in the context of React. |
I mean...that's completely reasonable, but then we're left with using two different history objects for app navigation within the react context and outside it, right? Or am I missing something? (I might be missing something.) |
@mjackson that is a shame. Redux-Saga is not exactly a niche library within the React ecosystem (and it's not just sagas, it affects anything that is not directly within a component). I guess we will continue to smuggle a reference to the history object into the window object as a workaround |
Unfortunately I must agree with @cinnabarcaracal here. I do see the train of thought that React Router is made for React. But indeed, redux saga is very common component in people's React stack. |
The goal with history v5 is to make this library just an implementation detail of React Router v6. Nowhere in the RR v6 API do you get a handle on the I understand that may be frustrating to some, but we are carrying around a lot of extra weight in history v4 that just isn't needed in v5 (basenames were a big culprit), and shedding 50% size here makes a huge difference in the overall size of a React Router app. I think the vast majority of RR users will appreciate the size savings in v6. That being said, the most common use case that I can think of for using history with redux-saga is for navigation. i.e. someone clicks a link/submits a form, you show a spinner while it's validating/submitting, then you navigate to the next page. Even though you can't use the function MyPage() {
let navigate = useNavigate();
function handleSubmit(event) {
// dispatch your Redux stuff here and pass along the navigate()
// function so you can call it when you're done
dispatchFormSubmit(event.currentTarget, navigate);
}
return (
<div>
<form onSubmit={handleSubmit}>...</form>
</div>
);
} One other nice thing about treating this library as an implementation detail is that someday it will hopefully just go away and you won't even need to care. There is a lot of work being done right now on a new Anyway, like I said I know this is going to require some work but I just wanted y'all to know that we are doing it for a reason and hopefully adapting to this kind of architecture will better prepare you for the future. |
@mjackson thanks for the thoughtful reply. Truth be told, I hate navigating inside redux saga (if for no other reason than it's too far removed from the components), but for a while there was no other place to really handle complex business logic within a React app--eg if you needed to do a form post, and then conditionally post or get more data based on the reply, redux saga was your best bet. Now that async await is finally mature enough to use, I've been using that as often as I can, but holy cats there's a lot of legacy code in redux saga that may take years to convert over. But yes, passing down a navigate object might be the most reasonable approach so long as redux saga is around, at least for me. |
This is more of a RR6 question but is related to history, if in RR6 you can't access the history object how do you recommend setting up microfrontends if multiple react apps need access to push / the current route but maybe aren't rendered in the same tree? |
is this working for basename? |
Hey @abinasp you're probably better off using |
@mjackson is it gonna be possible in RRv6 to dinamically change the basename in My use case is a simple language and region separation of my app depending on the IP of the user, then, depending on a selector of languages, I would like to be able to change the basename dinamically and start navigation from that new basename. Thanks in advance for the answer! |
This broke things for me now when and upgrade to react 18 and router v6 forced me to change from connected-react-router to redux-first-history. Setting the basename in rr v6 doesn't help me. My app needs to figure out its webcontext path, which may be "/oldalbum" or "/" (if called through a reverse proxy) and it previously set this as the basename of the history. I used connected-react-router to do programmatic navigation with redux actions from swipe actions. But connected-react-router doesn't work with react 18 and react router 6 so I had to switch to redux-first-history. At first the router paths didn't work at all, but then I figured out where react router v6 sets its basename and things started to work when clicking on links. But when I tried swipe navigation the "/oldalbum" prefix was gone. So I compared the path in LOCATION_CHANGE redux actions in the pre react v18/rr v6 version of the app and in the post react v18/rr v6 version. In the pre react v18/rr v6 version both LOCATION_CHANGE events created by react router navigation, and LOCATION_CHANGE sent with push() lacks the "/oldabum" prefix. In the post react v18/rr v6 version LOCATION_CHANGE events created by router navigation has the "/oldalbum" prefix, and the ones sent with push() lacks "/oldalbum", so they navigate to a non-existing page and 404s. I thought redux-first-history was to blame, but it looks like this change to history was the cause. Unsure how to proceed with this? Maybe lock history on v4 forever? |
I used to create a new history with a basename, this does not seem to be an option anymore.
So how do I set a basename in History v5?
The text was updated successfully, but these errors were encountered: