I've implemented google analytics into a react app. The problem is that it just shows the page title upon every page view. What's really strange is that when I disable the code to update upon page change, it still registers each url change as a new pageview in Google Analytics (With the same page title as always). This is navigating to pages using the react-router-dom, not hard refreshed, but the pageviews are still showing in GA!
Here's the code I'm using
import { useLocation } from "react-router-dom";
import { useEffect, useState, createContext } from "react";
export const GoogleAnalyticsContext = createContext(null);
function GoogleAnalyticsProvider({children}) {
const [gaInitialized, setGaInitialized] = useState(false);
const [gaTrackingCode, setGaTrackingCode] = useState();
const location = useLocation();
useEffect(() => {
//Fetch tracking code
setGaTrackingCode('[my tracking code]')
}, [])
useEffect(() => {
if (gaTrackingCode) {
console.log("Updating script");
const script1 = document.createElement('script');
script1.async = true;
script1.src = `https://www.googletagmanager.com/gtag/js?id=${gaTrackingCode}`
document.body.appendChild(script1);
const script2 = document.createElement('script');
script2.async = true;
script2.text = `
window.dataLayer = window.dataLayer || [];
function gtag(){window.dataLayer.push(arguments);}
window.gtag = gtag;
gtag('js', new Date());
gtag('config', '${gaTrackingCode}', {
cookie_flags: 'SameSite=None;Secure'
});
`
document.body.appendChild(script2);
setGaInitialized(true);
}
}, [gaTrackingCode])
// useEffect(() => {
// if (gaInitialized) {
// window.gtag('config', gaTrackingCode, {
// page_title: location.pathname + location.search,
// page_page: location.pathname + location.search
// })
// }
// }, [gaInitialized, location])
const store = {
gaInitialized
}
return <GoogleAnalyticsContext.Provider value={store}>{children}</GoogleAnalyticsContext.Provider>
}
export default GoogleAnalyticsProvider
Notice the commented out part. It's still registering new pageviews even with that commented out.
I'd wonder if Google has implemented some new feature, if it weren't for the fact that I've got the exact same setup running in a different environment, and this doesn't happen, and the pageviews work correctly (with the commented out code uncommented)
Edit: I've checked with console.log, and the initialization script is only being called once.
Edit2: I'm using a workaround. I can send events successfully, so I'm sending all the pageviews as events.
Related
I am undertaking one of the projects from frontendmentor.io
Whenever I click any of the mapped data, with the help of react-router-dom I am redirecting it to url/countryName.
In this scenario, I am using react-router-dom to fetch a particular country while using the useLocation hook to fetch the url, the problem is everything works fine in the first render but as soon as I reload the website the application breaks. No matter how many times I try it never renders again.
import axios from 'axios'
import React,{ useEffect,useState } from 'react'
import { useNavigate, useLocation } from 'react-router-dom'
const Country = () => {
const location = useLocation()
const name = location.pathname.split('/')[1]
const navigate = useNavigate()
const [country,setCountry] = useState({})
useEffect(() => {
const getCountry = async() => {
try {
const res = await axios.get(`https://restcountries.com/v3.1/name/${name.toLowerCase()}?fullText=true`)
console.log(res.data,res)
setCountry(res.data)
} catch (error) {
console.log(error)
}
}
getCountry()
}, [name])
console.log(country[0].name.common) // returns undefined after reload
const backButton = (event) => {
event.preventDefault()
navigate('/')
}
return (
<div className='country-page page'>
<button onClick={backButton} className='backButton'>back button</button>
<div className='country-page-layout'>
<h2></h2>
</div>
</div>
)
}
export default Country
Other resources:
error message screenshot
API which I am using : https://restcountries.com/
You need to wait for your data to be fetched first, and then render it.
const [country,setCountry] = useState({})
//country is an empty object
//but here, your are trying to get the value from the array. (But country is an empty object)
console.log(country[0].name.common) // returns undefined after reload
The solution is to check if your data is here first
if (country?.[0]?.name?.common){
console.log(country[0].name.common)
}
So I am currently working on a website in react, and I am trying to change the favicon dynamically.
I had found a snippet of code which I tried to adapt in order to make it work but clearly I'm still missing something. My website does not break but there are no changes.
Here is my code:
import Logo from './Images/logo.png';
function App() {
//need help
useEffect((url) =>{
let link = document.queryCommandValue("link[rel~={Logo}]");
if(!link){
link = document.createElement('link');
link.rel = 'icon';
document.getElementsByTagName('head')[0].appendChild(link);
}
link.href = url;
},[]);
return (
<div className="App">
You can do it using React's useEffect hook and some JavaScript. Usually, you would define your useEffect in your page components, so when your page is rendered, the effect triggers which updates the favicon.
const Page1 = (props) => {
// effect to update favicon
useEffect(() => {
let link = document.querySelector("link[rel~='icon']");
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
document.getElementsByTagName('head')[0].appendChild(link);
}
link.href = 'https://stackoverflow.com/favicon.ico';
}, []);
return (
<div>Page1</div>
);
}
Here's more on how to change favicon dynamically with just JavaScript
I'm getting started with Recoil for a React App, but running into some issues, or at least some behavior I'm not expecting.
I'd like to be able to use one component to render many different "views" based on the URL. I have a useEffect in this component that switches based on the location.pathname and based on that pathname, it'll make an API call. But before it makes the API call, it checks the length of the atom to see if it's empty or not, then will call the API and set the atom based on the API call.
However, when I navigate to a different URL and come back to one I've already visited, the API is called again, even though I've previously set the state for that URL.
The behavior I'm expecting is that once a URL has been visited and the return from the API is stored in an Atom, the API call isn't made again when leaving the URL and coming back.
Relevant code below:
Atom.js
export const reports = atom({ key: "reports", default: { country: [], network: [], }, });
the one component that will render different data based on the reports atom.
import { useRecoilState } from "recoil";
import { reports } from "../globalState/atom";
const TableView = ({ columns, }) => {
const location = useLocation();
const [report, setReport] = useRecoilState(reports);
const currentView = location.pathname.split("/")[1];
useEffect(() => {
const getReportsData = async () => {
switch (location.pathname) {
case "/network":
if (report[currentView].length === 0) {
const response = await fetch("/api");
const body = await response.json();
setReport(
Object.assign({}, report, {
[currentView]: body,
})
);
console.log('ran')
break;
}
getReportsData();
}, [])
As previously mentioned, that console.log is ran every time I navigate to /network, even if I've already visited that URL.
I've also tried doing this with selectors.
Atom.js
export const networkState = atom({
key: "networkState",
default: networkSelector,
});
export const networkSelector = selector({
key: "networkSelector",
get: async ({ get }) => {
try {
const body = await fetch("/api/network").then((r) => r.json());
return body;
} catch (error) {
console.log(error);
return [];
}
}
Component
import {useRecoilStateLoadable} from "recoil"
import {networkState} from "../globalState/atom";
const Table = ({columns}) => {
const [networkData, setNetworkData] =
useRecoilStateLoadable(networkState);
And then a switch statement based on networkData.state
}
Any help would be greatly appreciated, thank you!
I am trying to deepLinking and catching the url when url open the page on the screen which is functionally works when my app is not working on the background. However, it doesn't work if app is working on the background.
const isFocused = useIsFocused();
useEffect(() => {
getCode();
}, [isFocused]);
const getCode = async () => {
//we will generate a button in the forget password email, link will include a url ===> mobile://auth/new-password?verification=534396
const url = await Linking.getInitialURL();
console.log('url', url);
if (url?.includes('new-password')) {
//problem, it may not work if app is still working on the background
const query = queryString.parseUrl(url);
const verifyCode = query.query.verification;
setVerificationCode(String(verifyCode));
setIsLoading(false);
} else {
Alert.alert('Something went wrong');
}
};
When I directlinked to application with the link, it console log as "url null". Is my problem on the focusing part or on the getInitialUrl function?
I was experiencing a similar issue.
In my case we used linking from NavigationContainer and it would open the same X screen left on background regardless if the deeplink data had a different value for that screen.
I fixed it by using the getId on Stack.Screen:
const getId = ({ params }) => params?.id;
<Stack.Screen name="X" component={XComponent} getId={getId} />
You can find more info on getId here https://reactnavigation.org/docs/screen/#getid.
I've been working on a Next.JS web application for the past couple of days but I've reached a problem. The app has an API call (/api/settings) which returns some settings about the application from the database. Currently, I have a function which returns these settings and access to the first component:
App.getInitialProps = async () => {
const settingsRequest = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/settings`
);
const settingsResponse = await settingsRequest.json();
return { settings: settingsResponse };
};
This does work and I am able to pass in settings to components but there are two problems with this:
I need to nest the prop through many components to reach the components that I need
This request runs every time a page is reloaded/changed
Essentially, I need to create a system that does this:
runs a function in the _app.tsx getInitialProps to check if the data is already in localStorage, if not make the API request and update localStorage
have the localStorage value accessible from a custom hook.
Right now the problem with this is that I do not have access to localStorage from the app.tsx getInitialProps. So if anyone has an alternative to run this function before any of the page loads, please let me know.
Thanks!
I found a solution, it might be a janky solution but I managed to get it working and it might be useful for people trying to achieve something similar:
First we need to create a "manager" for the settings:
export const checkIfSettingsArePresent = () => {
const settings = localStorage.getItem("app_settings");
if (settings) return true;
return false;
};
export const getDataAndUpdateLocalStorage = async () => {
const r = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/settings`);
const response = await r.json();
localStorage.setItem("app_settings", JSON.stringify(response));
};
With that created we can add a UseEffect hook combined with a useState hook that runs our function.
const [doneFirst, setDoneFirst] = useState<boolean>(false);
useEffect(() => {
const settingsPreset = checkIfSettingsArePresent();
if (performance.navigation.type != 1)
if (settingsPreset) return setDoneFirst(true);
const getData = async () => {
await getDataAndUpdateLocalStorage();
setDoneFirst(true);
};
getData();
}, []);
//any other logic
if (!doneFirst) {
return null;
}
The final if statement makes sure to not run anything else before the function.
Now, whenever you hot-reload the page, you will see that the localStorage app_settings is updated/created with the values from the API.
However, to access this more simply from other parts of the app, I created a hook:
import { SettingsType } from "#sharex-server/common";
export default function useSettings() {
const settings = localStorage.getItem("app_settings") || {
name: "ShareX Media Server",
};
//#ts-ignore
return JSON.parse(settings) as SettingsType;
}
Now I can import useSettings from any function and have access to my settings.