React-Router Router.HistoryLocation refreshes the page - node.js

I am working on React-Express-Node application and focussing on SPA. I am using the react-router.
My server.js file looks like this (only routing part):
app.use(function(req, res, next) {
Router.run(routes,function(Handler, state) {
var ele = React.createElement(Handler);
res.render(path.join(__dirname + '/public/index'), {html: html});
});
next();
});
And the routes file has this code (pasting the main part):
module.exports = (
<Route name="app" path="/" handler={Main}>
<Route name="about" path="about" handler={About}/>
<Route name="about/id" path="about/:id" handler={About}/>
<DefaultRoute name="default" handler={Home} />
</Route>
);
And client.js looks like this:
Router.run(routes, function(Root,state){
React.render(<Root />,document.getElementById('app'));
});
This setup works fine without any problem.
Now, I want to use the History API pushstate so that i can have better urls and get rid of #. To do that, I added Router.HistoryLocation as the second parameter in client.js and it works, it removes the # and gives clean urls. But however, my page refreshes which I don't want.
I have searched this all over and found couple of solutions but they are either using Flux or a custom router. I am surely missing something related to state but not able to figure out. Can someone point me to the right direction ?

It is entirely possible to use Router.HistoryLocation with express server rendered SPA app (I'm doing it now).
You need to tell the server.js file where to get the history, i.e. req.url... like this.
Router.run(routes, req.url, function(Handler, state) {
var ele = React.createElement(Handler);
res.render(path.join(__dirname + '/public/index'), {html: html});
});
If the server is set up correctly, the next item to check is how you're transitioning to the routes. Using a traditional anchor <a/> tag will always refresh the page. React Router has provided their own <Link/> component that allows you to navigate to your routes without causing a page reload. You can also look into calling transitionTo() directly on your router to cause a navigation as well.
Also, when a <Link/> tag is active it will also pass an active class to the tag so css can style it accordingly.
Link
<Link to="route" props={} query={}>My Link</Link>
Navigation
router.transitionTo('route', params, query);
Check out the Link Docs and Navigation Docs.

This might help Meteor users:
import React, {Component} from 'react';
import {Link} from 'react-router';
export default class MenuItem extends Component{
render(){
return(
<li>
<Link to={this.props.link}>{this.props.page}</Link>
</li>
);
}
}
Import Link from react-router and use <Link /> to create your links will prevent the page from refreshing.
this.props.link and this.props.page are your dynamic data.

Related

Nuxt 3 re render component after API response

I am working on nuxt 3 and I need re render component after API response.
<template>
<AssetsTab
:form-data="formData.assets"
:loader="loader"
#on-submit="submitForm"
/>
</template>
<script setup>
onMounted( async () => {
await getDetails(); //API call
});
</script>
Here once the API call getDetails() successed. I need to rerender AssetsTab component again.
Use a reactive variable like const isApiRespond = ref(false) and make the variable true in the getDetails function when you get the successful API response. Then in the template use v-if like below,
<AssetsTab
v-if=isApiRespond
:form-data="formData.assets"
:loader="loader"
#on-submit="submitForm"
/>
Now if you again want to Re-Render your component then use a key prop in your component. Whenever your props value update, the component will auto-update.
<AssetsTab
v-if=isApiRespond
:key="rerendervalue"
:form-data="formData.assets"
:loader="loader"
#on-submit="submitForm"
/>
In above code whenever your key value update the whole component will auto update.
Here is a VuePlaygroud Link check it's console you will understand.

Linking to a different url using Link of react router dom [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 Make hyperlink to sub domain in TypeScript Execute File

I am trying to input a link pointing to a subdomain into the navigation menu of a react theme. I am a bit embarrassed to ask the community for something that is most likely very simple. However, I've had a hard time finding any relatable examples on the web to help guide me. Below is the code and how I thought it would work, however it doesn't since it uses the current path ahead of the hyperlink.
#RouteName.ts
export enum RouteName {
Home = "/"
About = "/About"
Service = "/Service"
Portfolio = "https://sub.domain.com/"
Contact = "/Contact"
}
#Navigation.tsx
const ROUTES = [
{url: RouteName.Home, name: "Home"},
{url: RouteName.About, name: "About"},
{url: RouteName.Service, name: "Service"},
{url: RouteName.Portfolio, name: "Portfolio"},
{url: RouteName.Contact, name: "Contact"},
];
<Menu className="Menu">
{ROUTES.map((item) => (
<li key={item.url} className={pathname === item.url ? "active" : ""}>
<Link to={item.url}>{item.name}</Link>
</li>
))}
</Menu>
#App.TSX
<Routes>
<Route path={RouteName.Portfolio} element = {
<>
<TopNav />
<Portfolio />
</>
}/>
.....etc
</Routes>
Result: localhost:3000/page-currently-on/https://sub.domain.com/
Need: https://sub.domain.com/
Link is a component from react Router and is meant for links inside your website. For external links, you can use a simple <a/> tag
Portfolio
It would probably be better to create a route on /Portfolio with a redirect like so:
<Route path='/Portfolio' component={() => {
window.location.href = 'https://sub.domain.com/';
return null;
}}/>
Then, you don't have to worry about it.
export enum RouteName {
Home = "/"
About = "/About"
Service = "/Service"
Portfolio = "/Portfolio"
Contact = "/Contact"
}
This solution creates makes the website easily expandable.
You have to use pathname when you want to link to the exact URL. In your case
<Link to={{pathname: item.url}} />
I know this doesn't play well with your types so have to trial and error a bit to implement it cleanly.
Source: https://thewebdev.info/2022/03/07/how-to-add-an-external-link-with-react-router/

Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function. react router v4

I am rather new to reactjs and was testing basic server side rendering with react-router v4 but I cannot get past this error, been trying since hours. I have tried every solution that I found on google but none of them seems to be working.
Here is the server.js code :
import Express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router'
import MyRoutes from './routes/routes.js';
...
app.get('*', (req, res) => {
let markup = `<!DOCTYPE html>
<html lang="en">
<body>
${renderToString(<StaticRouter location={req.url} context={{}}><MyRoutes/></StaticRouter>)}
</body>
</html>`;
res.write(markup);
res.end();
});
Problem seems to be with the following code :
./routes/routes.js code:
import React from 'react';
import { Match, Miss } from 'react-router';
const componentTest = () =>
(
<div>
Testing a component
</div>
);
export default () => (
<div>
<Match exactly={true} pattern="/" component={componentTest} />
</div>
);
Now if I remove the Match tag line I get blank page with no error.
But if that Match tag line is there I get the following error:
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in.
in Unknown
in Router (created by StaticRouter)
in StaticRouter
Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of `StatelessComponent`.
at invariant (/home/ubuntu/workspace/node_modules/react-dom/node_modules/fbjs/lib/invariant.js:44:15)
at instantiateReactComponent (/home/ubuntu/workspace/node_modules/react-dom/lib/instantiateReactComponent.js:74:56)
at instantiateChild (/home/ubuntu/workspace/node_modules/react-dom/lib/ReactChildReconciler.js:44:28)
at /home/ubuntu/workspace/node_modules/react-dom/lib/ReactChildReconciler.js:71:16
at traverseAllChildrenImpl (/home/ubuntu/workspace/node_modules/react-dom/lib/traverseAllChildren.js:77:5)
at traverseAllChildren (/home/ubuntu/workspace/node_modules/react-dom/lib/traverseAllChildren.js:172:10)
at Object.ReactChildReconciler.instantiateChildren (/home/ubuntu/workspace/node_modules/react-dom/lib/ReactChildReconciler.js:70:7)
at ReactDOMComponent.ReactMultiChild.Mixin._reconcilerInstantiateChildren (/home/ubuntu/workspace/node_modules/react-dom/lib/ReactMultiChild.js:187:41)
at ReactDOMComponent.ReactMultiChild.Mixin.mountChildren (/home/ubuntu/workspace/node_modules/react-dom/lib/ReactMultiChild.js:226:27)
at ReactDOMComponent.Mixin._createContentMarkup (/home/ubuntu/workspace/node_modules/react-dom/lib/ReactDOMComponent.js:653:32)
Any help would be much appreciated.
The solution to this was given by Tharaka Wijebandara in comments.
Problem is that Match has been replaced by Route in react-router v4. So replacing the Match tag line with this line solved it :
<Route exact path="/" component={componentTest} />
Source : https://reacttraining.com/react-router/web/api/Route

distinguish between first page load and router <Link />?

i have a route which renders an overlay modal.
i want it to animate-in if i navigate from the UI,
and be loaded as static when navigating from the address bar or after a refresh.
i use nodejs and react + react-router.
i thought about using my universal redux setup to match a state prop for this issue, but maybe there's a much more elegant solution
thanks!
This can be achieved by testing the prevPath property on the props.location object
const [isFirstLoad,setIsFirstLoad] = useState(false)
const {location} = props
useEffect(()=> {
props.prevPath ? setIsFirstLoad(true) : setIsFirstLoad(false)
}, [location])
return <Modal animate={!isFirstLoad}/>
Each render UseEffect will test the props.location (similar to componentWillReceiveProps()) to check if the previous path is different. On load the prevPath property won't exist, so it will be false.

Resources