react router - keep the query string on route change - reactjs

I would like to create routes that support query string.
When i say support i mean, passing it to the next route some how.
For example:
given this route: domain/home?lang=eng
and when moving to route domain/about i want it to keep the Query String and display domain/about?lang=eng.
I was sure there's a built in functionality for this but after reading the docs and a lot of search on the net, i couldn't find an elegant solution.
I'm using react-router#3.0.0 and react-router-redux#4.0.7

For react-router 4.x, try
const { history }
history.push('/about' + history.location.search)
To access this.props.history, make sure you have wrapped the component with withRouter HOC
import { withRouter } from 'react-router-dom'
...
export default withRouter(component)
refer https://github.com/ReactTraining/react-router/issues/2185

You will have to "forward" query param on each page transition - bothering and you can easily forgot to...
Instead, I would do this.
read stored/persisted lang preference. localStorage is good candidate here. Fallback to default language, when no preference is found
share lang via context, so that each and every component can read this value.
create some button (or whatever), which would modify this value
Since you are using redux, I would pull redux-persist to persist this preference across page reloads.

Related

React Router v6 - Handle both external hrefs and interal paths

In my react app with react-router v6 I am retrieving links dynamically that can be either a regular external url (like https://stackoverflow.com/) or an internal path (like "/my-page") that corresponds to a Route with react-router.
So far, when using react-router-dom's <Link> component, I could only get it to work with an internal path, but NOT with an external url. As far as I understood the docs I couldn't find a way to achieve this, at least not with v6.
So the best approach I've come up with so far is to write an own MyLink component that either render an <a> or a <Link> depending on whether the href is external or not:
function MyLink(props) {
const isHrefExternal = props.href.match(/^http|^https|^www/);
if (isHrefExternal) {
return <a href={props.href}>{props.children}</a>;
}
return <Link to={props.href}>{props.children}</Link>;
}
This of course doesn't look like a very good solution:
The check for isHrefExternal is very naive
It's unclear what props MyLink should accept and how they should be managed since <a> and <Link> have different props.
For a full example see this codesandbox
Can you tell me how to do it better? Ideally there would be an option to pass an external url to react-router-dom's <Link> component but I couldn't find one.
Thanks a lot!
The Link component handles only internal links within your app, and likely won't ever handle external links natively.
I don't see any overt issues with your code, and I don't think it's a bad solution. The "isExternal" check may be naive, but it only needs to cover your specific use cases. As new use cases are required you can tweak the logic for this check. In my projects I've worked with we've just typically included an isExternal type property with fetched data so the app doesn't even need to think about what is or isn't an external link, it just checks the flag when rendering.

How do you pass data from one view to the other via react-router-dom without using url parameters?

I use react-router-dom v 4.3.1 for client-side routing. I'm pretty new to React and can't figure out how to pass data from one view to the other without using url parameters. In Angular, the Angular router has a data property where you can pass data associated with a route. An example would be:
const appRoutes: Routes = [
{
path: 'hero/:id',
component: HeroDetailComponent,
data: { title: 'Hero Detail' }
},
];
Can you do the same in react-router-dom? If not, how would you recommend I pass data in React?
Thanks in advance for the help!
<Route path="hero/:id" render={() => <HeroDetailComponent title= "Hero Detail" />} />
Read this: Pass props to a component rendered by React Router
Or if you are using <Link> you can use pass through location object
<Link to={{ pathname: 'hero/:id', state: { title: 'Hero Detail'} }}>My route</Link>
Well you Could use the context API to create a sort of global AppState that you could update in your first component and use in your second component.
You could also abuse the localStorage API by setting a key with the data in the first component and getting it in the other.
However both of these are workarounds that Shouldn't have to be used. Why do you want to redirect to a page but not pass data to it using URL parameters.
There'a several solutions. React being a library, not a framework, doesn’t force you into a single one.
One way is to use the context api. It’s like a link to an object shared between different components.
Another one is redux, which uses context underneath, and gives you a single store for the whole app. You changes values dispatching actions to the store, so it’s a bit tricky to learn the first time.
Using a stream library would open up a lot of different options, but it’s harder to get into. Check refract if you want to go this way.
A poor man’s stream approach that may serve you is using document as a bus to pass data arround, using addEventListeners to receive data and dispatch new customEvent to send it.
Next is the simplest one of all, share a simple object. Using imports form your components, you can import the same object on both and that will be a single instance where data can be shared. Simple JavaScript. It’s not the react way though, because changes won’t trigger a repaint on the component.

When in component lifecycle should I get query params from URL?

I'm using React 16.4.1, React Router 4.3.1, and React Redux 5.0.7. I have a search route that can receive a query param like this:
https://example.com/search?q=foo
To be clear, React Router 4 discontinued support for location.query, so we're left having to manually parse query params from the location.search prop that React Router provides. We can use something like Javascript's URLSearchParams interface for this.
So I'd like a user to be able to visit the URL above and immediately begin a search for "foo". Therefore, I need to gather the q param at some point during page load. But when?
My first instinct was to have my Search component parse the query params during its componentDidMount lifecycle hook. That also happens to be the recommended hook for retrieving data from the server, something I'll do if the q param has a value.
But I've also considered moving that logic outside the component entirely to some JS file that generally runs on page load, like my app's index.js file. I have access to my Redux store there and could update the application state with the "searchText", and my Search component could then simply check for that prop (wired via Redux) during its mounting.
Gathering query params from the URL on page load - then taking action on them - is a relatively new problem for React developers, given that React Router handled it for us prior to version 4. But surely I'm not the first person to have to do this since version 4 was released. Is there an established pattern or best practice for this?
Thanks.
My approach would be to create an initialize folder along actions, reducers etc.. and create there functions like
export default (dispatch, getState) => {
dispatch(urlQueryParams());
// Some other initializers
};
const urlQueryParams = () => {
// return json to reducer with the params
}
Then on your main index file you can trigger it
import addQueryParamsInitialzer from 'redux/initialize/queryParam';
const store = configureStore(INITIAL_STATE);
addQueryParamsInitialzer(store.dispatch, store.getState);
That way you'll have it on your store no matter what component you're on

React router -- How to send props without query params or Redux?

I want to send data to another route, but don't want to send it in query params.
I don't want a new store for every route, nor do I want a store that simply holds all routes / params separately from where they are sent / consumed.
Is there a standard way to specify props for an upcoming route?
I found the solution on the react-router location api docs.
this.props.router.push({
pathname: '/view-user',
state: { userId }
});
This seems great for interstitial, standalone modal pages.
May need to specify a fallback if the state is missing, but haven't quite gotten that far.
if (!this.props.location.state) this.props.router.goBack();
or
const locations = this.props.location.pathname.split('/');
// do something
this.props.route.push(locations.join('/'));
If you are not sending the information in the query param, then you can put it in some other kind of store that can also be associated with the route.
You can wrap the router.push() call with your own function that takes an extra object you want to pass along. Something like...
function navigateTo(url, extraData) {
if (extraData !== undefined) {
saveExtraDataForRoute(url, extraData);
}
router.push(url);
}
In react-router, there is an onEnter prop associated with the route that specifies a function to call. The code in this function can retrieve the extra data and do whatever you want to do with it.
function onMyScreenEnter() {
const extraData = getExtraDataForRoute(url);
goCrazyNutsWithYourExtraData(extraData);
}
You'd supply the two functions saveExtraDataForRoute() and getExtraDataForRoute(). They could use your store (e.g. Redux), set values of a singleton object, or use LocalStorage. But essentially, to save the data so it's retrievable by URL later, you'd be saying something like:
extraDataStore[url] = extraData;
The other thing you may wish to look into is using a POST method with react-router. I haven't done this, and am not sure how well it works. Here is a link: How to Handle Post Request in Isomorphic React + React Router Application

React-router: Change URL and clear history

I have the following scenario:
A user opens an activation link; after the user has completed the activation process, the system will move them to another page.
I don't want to keep the activation link in the browser's history because when the the user goes back they will get to the activation step again.
How do I replace the history of a browser to remove certain requests from my application?
In ReactJs you should use browserHistory for this purpose. This takes care of your histories and you don't need to implement those functions on your own.
browserHistory has 2 methods push() and replace() which do the same functions as #fazal mentioned in his answer but in a better way.
So if you want to avoid user going back to previous state you would need to use browserHistory().replace
Start with importing it into your code:
import {browserHistory} from 'react-router'
After user has activated you do following:-
browserHistory.replace(//your new link)
HTML5 history API provide two methods to Adding and modifying history entries.
pushState() : back state preserved
replaceState() : no back state
Assuming you are using react-router.
So, on your Component use
this.props.history.replaceState('your new state')
read more: Manipulating the browser history
video: REACT JS TUTORIAL #6 - React Router & Intro to Single Page Apps with React JS
I know this is old but I had a similar issue and none of the other answers were 100% applicable with my version or React but this should work in more recent versions by clearing appended paths.
//replace(path, state)
this.props.history.replace("/home", "urlhistory");
Run this block where you want change route
history.entries = [];
history.index = -1;
history.push(`/route`);
This will clear the history and change for a new one.
window.location.href = 'Your path';
or
document.location.replace().

Resources