Check cookie is exist in React - reactjs

I have used js-cookie in my React application, here I just use a simple condition to check whether the cookie is matched or not and push history to the following route, here is my code:
const readCookie = () => {
let cookie = false;
if(cookie = Cookies.get("isLogged")){
history.push('/Homepage');
setAuthenticated(true);
}else{
history.push('/');
}
}
useEffect(() => {
readCookie()
},[]);
But the problem is, if I have many routes, so I just have to write many if conditions as much as my existing routes to check and push to that specific route, I think it doesn't make sense to do that. So, I just want to know that is there any easier way? To check the cookies and push to their paths or routes.

Yes, you need to create Public and Private Routes please follow the steps mentioned in the link below:
https://medium.com/wesionary-team/private-and-public-routes-with-react-router-e5f814ccff2f
and you can also check this repo
https://github.com/thanhbinhtran93/react-router-example

Related

How do I break the infinite redirect loop I've created in Next JS?

This application is being hosted on S3, entirely static (using next export), and we're routing all 404 errors to index.html in order to let those be handled on the client side so we can take advantage of dynamic routing. To handle this, I have the following in my _app.tsx file:
const { asPath, pathname, ...router } = useRouter();
// check if redirect
React.useEffect(() => {
if (pathname === '/' && asPath !== pathname) {
router.replace(asPath, undefined, { shallow: true });
}
}, [asPath]);
This works, for the dynamic routing aspect, but it introduces a new bug: when I navigate to a page that actually doesn't exist, like /fffff, there's an infinite loop of the app trying to reroute to /fffff. Ideally, it would only try to reroute once, and then default to the 404.tsx or _error.tsx page. I've tried creating a stateful boolean like hasRedirected and then just marking that as true in the useEffect, but that didn't work because the page is actually refreshing and thus resetting state on each router.replace call. How do I handle this error and break out of the loop?
update: The issue seems to be that when I call router.replace, Next doesn't find a path to match /fffff, so its default behavior is to try asking the server for the route by refreshing. I need to disable or intercept this behavior somehow.
The solution we ended up finding, which works quite well, uses session storage to store a hasRedirected variable that gets deleted after being read. Here's the code:
React.useEffect(() => {
if (router.isReady) {
const isRedirect = pathname === '/' && asPath !== pathname;
if (sessionStorage.getItem('hasRedirected')) {
sessionStorage.removeItem('hasRedirected');
if (isRedirect) router.replace('/404');
} else if (isRedirect) {
sessionStorage.setItem('hasRedirected', 'true');
router.replace(asPath);
}
}
}, [asPath, pathname, router.isReady]);

Next JS - Handling getInitialProps on _app.js in SSR vs CSR

I am trying to create a Next JS application that handles the authentication and initial routing inside getInitialProps. I discovered this method can be executed either in the server or on the client.
My approach so far it's to have 2 different handlers based on detecting if I am in executing in the server checking for the presence of the req attribute inside of ctx.
This does the trick but doesn't feel like is the right way of doing. Can somebody, please, tell me if there is a cleaner way.
All authentication is handled in a separate subdomain, so I just need to redirect to the auth subdomain if there is no cookie or auth request fails for some other reason.
import "../../styles/globals.css";
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
MyApp.getInitialProps = async (appContext) => {
let cookie, user;
let ctx = appContext.ctx;
//Check if I am in the server.
if (ctx.req) {
cookie = ctx.req.headers.cookie
//Do auth request.
//Redirect base on user properties
// handle redirects using res object
ctx.res.writeHead(302, { Location: "/crear-cuenta"});
} else {
cookie = window.document.cookie;
//Do auth request.
//Redirect base on user properties
//Do redirects using client side methods (useRouter hook, location.replace)???
}
//Return pageProps to the page with the authenticted user information.
return { pageProps: { user: user } };
};
export default MyApp;
I think your code is clean enough. Of course you still can maintain it.
My suggestion would be as the followings:
MyApp.getInitialProps = async (appContext) => {
in this line you can use object destructuring technique to get the context straightforward:
MyApp.getInitialProps = async ({ ctx }) => {
then you won't need this line for example anymore : let ctx = appContext.ctx;
The most important part of your code which can be cleaned up by the way is the area that you have written your auth request twice in an if/else condition. I would suggest you to implement that part like this:
const cookie = ctx.req ? ctx.req.headers.cookie : window.document.cookie;
Although I would try to keep everything in getInitialProps on server side, In that case I make a small change to get the cookie as following and process it in server-side only.
const cookie = cookie.parse(ctx.req ? ctx.req.headers.cookie || "" : undefined);
Note that: I'm using a cookie parser which u can install the package yourself as well. (npm install cookie)
if you need to do an extra check on your cookie at client side, I will do that in componentdidmount or in case you are using react hooks in useEffect. But it is not necessary.
Now you can implement //Do auth request once, which will cause cleaner code and of course to reduce unnecessary repetition.

Catching parameters in redirect with Gatsby.js

I have one quick and dirt question.
Is it possible to catch a query parameter from a server redirect inside of Gatsby.js application?
We have a Pardot tracking link that does redirect to our thank you page which is built in Gatsby.js and I want to pass some query parameters to the application it self from that redirect.
So for example:
www.trackedlink.com/thank-you?programme_code=CODE_FROM_REDIRECT_ON_SERVERSIDE
will redirect to:
www.gatsbyapplicationthatwillreadthequery.com/thank-you?programme_code=CODE_FROM_REDIRECT_ON_SERVERSIDE
Is it possible to read that query inside of the application if it's coming from the outside of the app?
Cheers and have a great week!
If they are triggered in the client-side the redirection will be caught by the application and yes, it would possible if they are coming from outside the app or using a standard anchor. Not using a #reach/router (<Link> component since it's a limitation).
A clean and scalable way to use it is by adding in the function in your gatsby-browser.js configuration:
import React from 'react';
import { checkUrlFunction } from './src/services/yourCheckUrlFunction';
export const onClientEntry = () => checkUrlFunction();
Adding a function in gatsby-browser.js with onClientEntry API will trigger your function once the page is loaded. From the documentation:
onClientEntry Function (_: emptyArg, pluginOptions: pluginOptions) => undefined Called when the Gatsby browser runtime first starts.
Your function should look like:
export const checkUrlFunction = () => {
if (typeof window !== 'undefined') {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const programmeCode= urlParams.get('programme_code')
if(programmeCode) window.localStorage.setItem('programmeCode', programmeCode)
console.log(programmeCode); // will output CODE_FROM_REDIRECT_ON_SERVERSIDE
};
};
Note the typeof window !== 'undefined' necessary to avoid issues if the window object is not defined when triggering the function
Hi Ferran, thank you for your solution but unfortunately, it does not
work when the redirect happens. It only works if the query string is
inside of the application
Yes, the idea of adding the function in gatsby-browser.js is to avoid the addition of checkUrlFunction() in each page, template, or component. The disadvantage is that you lose a bit of control but it saves a lot of overwriting code and improves the scalability and readability.
Thanks, Ferran, if you could show me an example of it - it would be
amazing! This cookie topic is sort of unknown water for me
So, with your specifications updated, I've added the localStorage approach since it's easier to achieve in a non-IDE environment like this, but the idea is exactly the same.
Set a vault (cookie or localStorage) automated in the gatsby-browser.js function
if(programmeCode) window.localStorage.setItem('programmeCode', programmeCode)
This sets a localStorage key/value pair ('programmeCode' (key)/programmeCode (value)
Access to that vault in your component. Use a componentDidMount lifecycle or useEffect hook to ensure that is loaded before the DOM tree is mounted.
useEffect(()=>{
if(typeof window !== undefined) console.log(window.location.getItem('programmeCode')
}, [])

React Router: Go back to specific page

I have a settings page, with a few sub routes:
--/home
--/articles
--/settings
--/Account(default)
--/Help
--/About
I would like it in such a way: when I'm in any one of the sub pages under settings, and the browser go back in history, it should always go to the (default)Account page. Going back again will follow the normal browser history behavior.
What's the best way of handling it?
You can use history:
import createHistory from "history/createBrowserHistory"
const history = createHistory()
history.push("/account")
<Router history={history}/>
After quite some research, it seems there's no easy way of doing it, discussions in react-router github wouldn't disagree. A workaround seems to be the consensus. Which is a bit raw to my liking, but anything that works is good.
So instead of keeping every sub page under settings with their own route(to be handled by react-router), I have them controlled by state in settings component. And the following event handling is used:
componentDidMount = () => {
window.onpopstate= () => {
if(this.state.currentSubPage !== 'account-settings') {
this.props.history.push('/settings');
} else {
window.onpopstate = null;
}
};
}

How to track pageviews on React

So basically the problem i have is that my site is using reactjs so it doesn't reload and i need to detect the change on the URL in order to send a new pageview to Google Analytics. Has anyone deal with this before?
EDIT: Maybe i was a little unclear, im using Google Tag Manager and i have no control over the code on the page but i can request for dataLayers.
The obvious solution is to ask for a dataLayer when the page changes but since i would like to avoid doing this i was strictly asking if anyone knows a way to detect this kind of changes on the DOM from GTM.
For single page apps, you can track 'virtual' pageviews like given in the docs:
When the page changes, do
ga('set', 'page', '/new-page.html');
After this point, if your do a 'send', it will track this page.
ga('send', 'pageview');
I'd suggest you use something like react-ga for doing it a little more conveniently, it has functions like
ReactGA.pageview('/about/contact-us');
and ReactGA.modalview('/about/contact-us');
You need to look for all the paths using a Route like this,
<Route path="/" component={updateTracking} />
<Switch>
..... //further actual routes
</Switch>
and then send the pageview using the global Google Analytics (ga) method (accessible through the window object) using window.location.pathname
const updateTracking = () => {
window.ga('send', 'pageview', {
page: window.location.pathname
});
}
Note: You need to put the tracking code you got from Google Analytics in the main HTML (index.html) for it all to work.
Alternatively, you can listen for createBrowserHistory changes and send a pageview event.
import { createBrowserHistory } from 'history'
const history = createBrowserHistory()
history.listen(() => {
window.ga('send', 'pageview', {
page: window.location.pathname
})
}
)
The useLocation hook was added in 5.1.0 & can be used:
In your App.js, import the following
import React, { useState } from 'react';
import { useLocation } from "react-router-dom";
The following assumes you have created the react application using create-react-app or some other mechanism to pick GA_ID in your application from ENV
In your Environment variables, make sure you have : REACT_APP_GA_ID
function initialiseAnalytics() {
const TRACKING_ID = process.env.REACT_APP_GA_ID;
ReactGA.initialize(TRACKING_ID);
}
function usePageTracking() {
const location = useLocation();
const [initialized, setInitialized] = useState(false);
useEffect(() => {
initialiseAnalytics();
setInitialized(true);
}, []);
useEffect(() => {
if (initialized) {
ReactGA.pageview(location.pathname + location.search);
}
}, [initialized, location]);
}
Finally, in your App function, call the page tracking -
function App() {
usePageTracking();
// Other Code Goes Here
return (
<div className="App">
<p>Sample Application</p>
</div>
);
}
Anyone still looking for the answer, here it is.
This is the expected behavior for Single Page Application(SPA) like React.
Solution is to update the Tracker when your route changes
You can deal with this problem in two ways
1- In all your route containers use set command from ga and update the tracker
ga('set', 'page', '/new-page.html');
2- In all your route containers use send command from ga and update the tracker
ga('send', 'pageview', '/new-page.html');
The second solution using send command is not recommended by Google Analytics
This is because fields passed via the send command are not set on the tracker—they apply to the current hit only. Not updating the tracker will cause problems if your application sends any non-pageview hits (e.g. events or social interactions), as those hits will be associated with whatever page value the tracker had when it was created.
Ref: Google Anaylytics

Resources