distinguish between first page load and router <Link />? - node.js

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.

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.

handlebars - add content to head of view from partial

I am using express-handlebars in my project and have the following problem:
Question
I want to be able to add <script> oder such tags to my overall views head from a partial that is called inside the view.
Example:
The view
{{#layout/master}}
{{#*inline "head-block"}}
<script src="some/source/of/script">
{{/inline}}
...
{{>myPartial}}
{{/layout/master}}
The view is extending another partial (layouts/master) that I use as a layout. It adds its content to that ones head block through the inline partial notation, which works fine
the Partial "myPartial
<script src="another/script/src/bla"></script>
<h1> HELLO </h1>
Now I would like that particular script tag in there to be added to my views head-block. I tried going via #root notation but can only reference context there. Not change anything.
I know I could use jquery or similar to just add the content by referencing the documents head and such. But I wanted to know if this is possible at all via Handlebars.
I do doubt it is in any way. But if you have any ideas or suggestions, please do send them my way! Many thanks!!!
UPDATE
This wont work if you have more than one thing injected into your layout / view. Since this happens when the browser loads the page, it creates some kind of raceconditions where the helpers has to collect the things that have to be injected into the parent file. If its not quick enough, the DOMTree will be built before the helper resolves. So all in all, this solution is NOT what I hoped for. I will research more and try to find a better one...
Here is how I did it. Thanks to Marcel Wasilewski who commented on the post and pointed me to the right thing!
I used the handlebars-extend-block helper. I did not install the package, as it is not compatible with express-handlebars directly (Disclaimer: There is one package that says it is, but it only threw errors for me)
So I just used his helpers that he defines, copied them from the github (I am of course linking to his repo and crediting him!) like so:
var helpers = function() {
// ALL CREDIT FOR THIS CODE GOES TO:
// https://www.npmjs.com/package/handlebars-extend-block
// https://github.com/defunctzombie/handlebars-extend-block
var blocks = Object.create(null);
return {
extend: function (name,context) {
var block = blocks[name];
if (!block) {
block = blocks[name] = [];
}
block.push(context.fn(this));
},
block: function (name) {
var val = (blocks[name] || []).join('\n');
// clear the block
blocks[name] = [];
return val;
}
}
};
module.exports.helpers = helpers;
I then required them into my express handlebars instance like so:
let hbsInstance = exphbs.create({
extname: 'hbs',
helpers: require('../folder/toHelpers/helpersFile').helpers() ,
partialsDir: partialDirs
});
Went into my central layout/master file that`is extended by my view Partial and added this to its <head> section
{{{block 'layout-partial-hook'}}}
(The triple braces are required because the content is HTML. Else handlebars wont recognize that)
Then in the partial itself I added things like so:
{{#extend "layout-partial-hook"}}
<link rel="stylesheet" href="/css/index.css"/>
{{/extend}}
And that did the trick! Thanks!!!

Vue component not rendered inside router-view

recently I've been using vue in frontend and vue-router with it to shape a SPA.
My problem is that I am not able to access a component defined in main Vue instance:
import ElementComponent from './Element';
const app = new Vue({
el: '#app',
router,
components: { Element: ElementComponent }
});
Whenever I do <element></element> within the #app scope the component gets rendered but I can not use the element inside a route component.
My routes are defined like this:
var routes = [
{ path: '/section/part', component: require('../views/Part') }];
And then provided to router instance:
new VueRouter({routes});
Breakpoint whenever I try to call <element></element> inside Part component template I get this in vuedevtools: [Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.
(found in at C:\Users\Doe\project\js\views\Part.vue)
You need to import the component into the views/Part component. So do the same thing that you did in the main Vue instance, but only in the part component.
I believe if you want to make components global you need to do use
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
reference https://v2.vuejs.org/v2/guide/components.html#Registration

React-Router Router.HistoryLocation refreshes the page

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.

Resources