react router v4 programmatically change route - reactjs

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;

Related

Building portfolio website and I want to include my github link [duplicate]

Since I'm using React Router to handle my routes in a React app, I'm curious if there is a way to redirect to an external resource.
Say someone hits:
example.com/privacy-policy
I would like it to redirect to:
example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies
I'm finding exactly zero help in avoiding writing it in plain JavaScript at my index.html loading with something like:
if (window.location.path === "privacy-policy"){
window.location = "example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies"
}
Here's a one-liner for using React Router to redirect to an external link:
<Route path='/privacy-policy' component={() => {
window.location.href = 'https://example.com/1234';
return null;
}}/>
It uses the React pure component concept to reduce the component's code to a single function that, instead of rendering anything, redirects browser to an external URL.
It works both on React Router 3 and 4.
With Link component of react-router you can do that. In the "to" prop you can specify 3 types of data:
a string: A string representation of the Link location, created by concatenating the location’s pathname, search, and hash properties.
an object: An object that can have any of the following properties:
pathname: A string representing the path to link to.
search: A string representation of query parameters.
hash: A hash to put in the URL, e.g. #a-hash.
state: State to persist to the location.
a function: A function to which current location is passed as an argument and which should return location representation as a string or as an object
For your example (external link):
https://example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies
You can do the following:
<Link to={{ pathname: "https://example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies" }} target="_blank" />
You can also pass props you’d like to be on the such as a title, id, className, etc.
There isn’t any need to use the <Link /> component from React Router.
If you want to go to external link use an anchor tag.
<a target="_blank" href="https://meetflo.zendesk.com/hc/en-us/articles/230425728-Privacy-Policies">Policies</a>
It doesn't need to request React Router. This action can be done natively and it is provided by the browser.
Just use window.location.
With React Hooks
const RedirectPage = () => {
React.useEffect(() => {
window.location.replace('https://www.google.com')
}, [])
}
With React Class Component
class RedirectPage extends React.Component {
componentDidMount(){
window.location.replace('https://www.google.com')
}
}
Also, if you want to open it in a new tab:
window.open('https://www.google.com', '_blank');
I actually ended up building my own Component, <Redirect>.
It takes information from the react-router element, so I can keep it in my routes. Such as:
<Route
path="/privacy-policy"
component={ Redirect }
loc="https://meetflo.zendesk.com/hc/en-us/articles/230425728-Privacy-Policies"
/>
Here is my component in case anyone is curious:
import React, { Component } from "react";
export class Redirect extends Component {
constructor( props ){
super();
this.state = { ...props };
}
componentWillMount(){
window.location = this.state.route.loc;
}
render(){
return (<section>Redirecting...</section>);
}
}
export default Redirect;
Note: This is with react-router: 3.0.5, it is not so simple in 4.x
I went through the same issue. I want my portfolio to redirect to social media handles. Earlier I used {Link} from "react-router-dom". That was redirecting to the sub directory as here,
Link can be used for routing web pages within a website. If we want to redirect to an external link then we should use an anchor tag. Like this,
Using some of the information here, I came up with the following component which you can use within your route declarations. It's compatible with React Router v4.
It's using TypeScript, but it should be fairly straightforward to convert to native JavaScript:
interface Props {
exact?: boolean;
link: string;
path: string;
sensitive?: boolean;
strict?: boolean;
}
const ExternalRedirect: React.FC<Props> = (props: Props) => {
const { link, ...routeProps } = props;
return (
<Route
{...routeProps}
render={() => {
window.location.replace(props.link);
return null;
}}
/>
);
};
And use with:
<ExternalRedirect
exact={true}
path={'/privacy-policy'}
link={'https://example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies'}
/>
The simplest solution is to use a render function and change the window.location.
<Route path="/goToGoogle"
render={() => window.location = "https://www.google.com"} />
If you want a small reusable component, you can just extract it like this:
const ExternalRedirect = ({ to, ...routeProps }) => {
return <Route {...routeProps} render={() => window.location = to} />;
};
and then use it (e.g. in your router switch) like this:
<Switch>
...
<ExternalRedirect exact path="/goToGoogle" to="https://www.google.com" />
</Switch>
I had luck with this:
<Route
path="/example"
component={() => {
global.window && (global.window.location.href = 'https://example.com');
return null;
}}
/>
I solved this on my own (in my web application) by adding an anchor tag and not using anything from React Router, just a plain anchor tag with a link as you can see in the picture screenshot of using anchor tag in a React app without using React Router
Basically, you are not routing your user to another page inside your app, so you must not use the internal router, but use a normal anchor.
Although this is for a non-react-native solution, but you can try.
In React Router v6, component is unavailable. Instead, now it supports element. Make a component redirecting to the external site and add it as shown.
import * as React from 'react';
import { Routes, Route } from "react-router-dom";
function App() {
return(
<Routes>
// Redirect
<Route path="/external-link" element={<External />} />
</Routes>
);
}
function External() {
window.location.href = 'https://google.com';
return null;
}
export default App;
In React Route V6 render props were removed. It should be a redirect component.
RedirectUrl:
const RedirectUrl = ({ url }) => {
useEffect(() => {
window.location.href = url;
}, [url]);
return <h5>Redirecting...</h5>;
};
Route:
<Routes>
<Route path="/redirect" element={<RedirectUrl url="https://google.com" />} />
</Routes>
I think the best solution is to just use a plain old <a> tag. Everything else seems convoluted. React Router is designed for navigation within single page applications, so using it for anything else doesn't make a whole lot of sense. Making an entire component for something that is already built into the <a> tag seems... silly?
To expand on Alan's answer, you can create a <Route/> that redirects all <Link/>'s with "to" attributes containing 'http:' or 'https:' to the correct external resource.
Below is a working example of this which can be placed directly into your <Router>.
<Route path={['/http:', '/https:']} component={props => {
window.location.replace(props.location.pathname.substr(1)) // substr(1) removes the preceding '/'
return null
}}/>
I don't think React Router provides this support. The documentation mentions
A < Redirect > sets up a redirect to another route in your application to maintain old URLs.
You could try using something like React-Redirect instead.
I was facing the same issue and solved it using by http:// or https:// in React.
Like as:
<a target="_blank" href="http://www.example.com/" title="example">See detail</a>
You can use for your dynamic URL:
<Link to={{pathname:`${link}`}}>View</Link>
For V3, although it may work for V4. Going off of Eric's answer, I needed to do a little more, like handle local development where 'http' is not present on the URL. I'm also redirecting to another application on the same server.
Added to the router file:
import RedirectOnServer from './components/RedirectOnServer';
<Route path="/somelocalpath"
component={RedirectOnServer}
target="/someexternaltargetstring like cnn.com"
/>
And the Component:
import React, { Component } from "react";
export class RedirectOnServer extends Component {
constructor(props) {
super();
// If the prefix is http or https, we add nothing
let prefix = window.location.host.startsWith("http") ? "" : "http://";
// Using host here, as I'm redirecting to another location on the same host
this.target = prefix + window.location.host + props.route.target;
}
componentDidMount() {
window.location.replace(this.target);
}
render(){
return (
<div>
<br />
<span>Redirecting to {this.target}</span>
</div>
);
}
}
export default RedirectOnServer;
I am offering an answer relevant to React Router v6 to handle dynamic routing.
I created a generic component called redirect:
export default function Redirect(params) {
window.location.replace('<Destination URL>' + "/." params.destination);
return (
<div />
)
}
I then called it in my router file:
<Route path='/wheretogo' element={<Redirect destination="wheretogo"/>}/>
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
function App() {
return (
<Router>
<Route path="/" exact>
{window.location.replace("http://agrosys.in")}
</Route>
</Router>
);
}
export default App;
Using React with TypeScript, you get an error as the function must return a React element, not void. So I did it this way using the Route render method (and using React router v4):
redirectToHomePage = (): null => {
window.location.reload();
return null;
};
<Route exact path={'/'} render={this.redirectToHomePage} />
Where you could instead also use window.location.assign(), window.location.replace(), etc.
Complementing Víctor Daniel's answer here: Link's pathname will actually take you to an external link only when there's the 'https://' or 'http://' before the link.
You can do the following:
<Link to={{ pathname:
> "https://example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies"
> }} target="_blank" />
Or if your URL doesn't come with 'https://', I'd do something like:
<Link to={{pathname:`https://${link}`}} target="_blank" />
Otherwise it will prepend the current base path, as Lorenzo Demattécommented.
If you are using server-side rending, you can use StaticRouter. With your context as props and then adding <Redirect path="/somewhere" /> component in your app. The idea is every time React Router matches a redirect component it will add something into the context you passed into the static router to let you know your path matches a redirect component.
Now that you know you hit a redirect you just need to check if that’s the redirect you are looking for. then just redirect through the server. ctx.redirect('https://example/com').
You can now link to an external site using React Link by providing an object to to with the pathname key:
<Link to={ { pathname: '//example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies' } } >
If you find that you need to use JavaScript to generate the link in a callback, you can use window.location.replace() or window.location.assign().
Over using window.location.replace(), as other good answers suggest, try using window.location.assign().
window.location.replace() will replace the location history without preserving the current page.
window.location.assign() will transition to the URL specified, but will save the previous page in the browser history, allowing proper back-button functionality.
location.replace()
location.assign()
Also, if you are using a window.location = url method as mentioned in other answers, I highly suggest switching to window.location.href = url.
There is a heavy argument about it, where many users seem to adamantly want to revert the newer object type window.location to its original implementation as string merely because they can (and they egregiously attack anyone who says otherwise), but you could theoretically interrupt other library functionality accessing the window.location object.
Check out this conversation. It's terrible.
JavaScript: Setting location.href versus location
I was able to achieve a redirect in react-router-dom using the following
<Route exact path="/" component={() => <Redirect to={{ pathname: '/YourRoute' }} />} />
For my case, I was looking for a way to redirect users whenever they visit the root URL http://myapp.com to somewhere else within the app http://myapp.com/newplace. so the above helped.

How to allow user to save inputs for further review

I have made a small calculating website and I would like the authenticated user to save his/her calculations.
Overview:
I have a main page with 8 input fields and several labels/graphs displaying results information. Whenever you change one of the inputs, the labels and graphs update thanks to hooks.
The goal: loading data
I made a "Save" button, which, when clicked :
saves all the inputs to firebase (this works fine already)
create a link in /myaccount/dashboard, which redirects you to the main page with your saved inputs (that I need help with)
I am having a hard time finding for resources online. However, while looking for responsive calculating websites, I came across this one : https://optionstrat.com
Even-thought I have no idea what they are calculating it does what I'm looking for, ie : you can "save a trade" and then go to you account where all the saved trades are displayed.
Does anyone know a good tutorial how to do so ?
Thanks you :)
Edit
This is my save function in my App.js:
function Savecalc(){
const calcRef = db.ref("User_"+auth.currentUser.uid);
const newCalc = calcRef.push();
newCalc.set({inputs:{a,b,c},outputs:{x,y}});
/* Then attribute an URL to a saved calculation*/
}
Then, in my Dashboard.js, I would have:
const db=app.database();
export default function Dashboard() {
/* getting the user calculations */
return (
<div>
<!-- Mapping of the user's calculations -->
</div>
)
I suspect what you are probably looking for is a little library called react-router-dom. This library essentially provides a collection of navigational components that you can employ to navigate around your app. Here is a basic example. Once you install it into your project, you should create a separate AppRouter.js file that might resemble something along the lines:
import React from 'react';
// Install the react-router-dom package
import { Router, Route, Switch } from 'react-router-dom';
// Further install the history package
import { createBrowserHistory } from 'history';
// Import your dashboard component and all other components you wish to create a route to
// This is just an example
import HomePage from '../components/Homepage';
import Dashboard from '../components/Dashboard';
// You will need to create a Page Not Found component that redirects when a wrong URL is inserted
import NotFoundPage from '../components/NotFoundPage';
export const history = createBrowserHistory();
const AppRouter = () => (
<Router history={history}>
<Switch>
<Route path='/' component={HomePage} exact={true} />
<Route path='/myaccount/dashboard' component={Dashboard} />
<Route component={NotFoundPage} />
</Switch>
</Router>
);
export default AppRouter;
Then in your main/app.js file you will need to add the AppRouter component. It should look something like this:
const Application = () => (
<Provider store={store}> // if using react-redux, otherwise ignore Provider
<AppRouter />
</Provider>
);
ReactDom.render(<Application />, document.getElementById('app'));
You can also create public and private routes (e.g. accessible only for signed in users). Examples of these can be found here.
If you are saving the user's calculations, you could then query them on whatever page you want to display them on e.g. /myaccount/dashboard.
You can then map over them and display them in the UI as links, where the link could be something like /myaccount/dashboard/YlxIJ2zOxI9KYJ5Dag6t where YlxIJ2zOxI9KYJ5Dag6t is the auto-generated document ID from Firestore.
Assuming you are using React Router, you can then have a route such as:
<Route exact path="/myaccount/dashboard/:id">
On this page, you can get the ID of the document from the parameters using React Router's useParams hook like so:
const { id } = useParams();
You can then query the specific calculation's info in useEffect, and display them however you want.
Let me know if this is what you need, or if you need more help with any step!

unable to use Route children vs Route component with react-router-dom

I saw that using <React component> and <React render>, may be deprecated in react-router-dom 6.x, so i followed the migration steps of removing the component property, and instead nesting the component as a child of the <Route > node. it worked fine until i got to my routes that use dynamic parameters, and it threw an error.
i changed from...
<Route exact path={`/:category`} component={CategoryPage} />
to...
<Route exact path={`/:category`}>
<CategoryPage />
</Route>
the error is
Exception has occurred: TypeError: Cannot read property 'params' of undefined
at selectParams
it seems pretty straightforward, so is there anything else i need to look at that might be causing this?
UPDATE
i am using useParams in my categoryPage, which works fine while using <React component>
const CategoryPage = () => {
const { category } = useParams()
...
When you make use of children or elements props as specified in react-router v6, the components rendered are not passed the router props directly so you have to make use of useParams, useNavigate, useLocation or useRouteMatch hooks depending on your useCase in the child component if you child component is a functional component
If you child component is a class component, you will make use of withRouter HOC
For your case useParams hook is required to get access to the category param
Please check out this migration guide from v5 to v6 for more details

using React Router with browserHisory

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

Is it possible to access the current route component with React Router

For example I have routes like:
<Route path="about" component={AboutPage} />
<Route path="status" component={StatusPage} />
Lets assume that the current route is '#/about'. I need something like routerInstance.currentComponent to return AboutPage component instance.
Does React Router has a method to do it?
ps. I understand that accessing component instances from the outside is not the React Way to do things, but nevertheless I need it.
not sure if react-router already exposes it, theres an old GitHub discussing undocumented api's, here but you could set it yourself per Route.
//AboutPage
componentDidMount() {
global.currentComponent = this
}
//StatusPage
componentDidMount() {
global.currentComponent = this
}

Resources