Dynamically changing favicon in react.js - reactjs

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

Related

React Google Analytics Pageview showing without any code

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.

NextJs script component should hide on a specific page

In my React/NextJS app I added a new component to show a FreshDesk widget.
This component should only show on some specific pages after a login but when I logout from the app the widget is still visible until I refresh the page
The component
import Script from 'next/script';
const FreshDesk = () => {
const { freshDesk } = useConfig();
return (
<>
{console.log(freshDesk)}
<Script
src={`https://widget.freshworks.com/widgets/${freshDesk}.js`}
strategy="lazyOnload"
/>
<Script id="my-script" strategy="afterInteractive">
{` window.fwSettings={
'widget_id':${freshDesk},
};
!function(){if("function"!=typeof window.FreshworksWidget){var n=function(){n.q.push(arguments)};n.q=[],window.FreshworksWidget=n}}()
`}
</Script>
</>
);
};
export default FreshDesk;
It is just called <FreshDesk /> when it needs it.
The login page doesn't have the widget call but soon as I log out the widget is still there until I refresh and don't know what to do about it.
It looks like you can use FreshworksWidget("destroy") to destroy the widget.
source
I would propose you add the following useEffect (disclaimer: I don't know the library so this might not work perfectly)
useEffect(()=>{
// create the widget if the script is loaded already
if(window.FreshworksWidget){
window.FreshworksWidget("boot");
}
// when unmounting the component, destroy the FreshworksWidget
return ()=>{
if(window.FreshworksWidget){
window.FreshworksWidget("destroy");
}
}
}, []);
Decided to answer my own question by showing the solution I found working for me.
What I did was to add in the login component this code
useEffect(() => {
if (document.querySelector('#launcher-frame')) {
document.querySelector('#launcher-frame').style.display = 'none';
}
}
and in the component, I was using the widget
useEffect(() => {
if (document.querySelector('#launcher-frame')) {
document.querySelector('#launcher-frame').style.display = 'block';
}
}, []);
In this way, I just hide it from my login page but showing everywhere else

Component state and hooks not updated after browser previous button visit

When doing a window.location.href (when submitSuccess is true) and navigate back with the browser previous button, my component does not re-render. So the submitSuccess and submitting state variables are still on true, while I need them to be false again when revisiting the application without a refresh of the page.
const MultipleStepForm = () => {
const [submitting, setSubmitting] = useState(false);
const [submitSuccess, setSubmitSuccess] = useState(false);
useEffect(() => {
console.log("Running!");
},[]);
useEffect(() => {
successFunction();
}, [submitSuccess]);
const successFunction = () => {
window.location.href = "https://www.example.com/other-page/";
};
return (
<div>
{(submitting) && (
<div>Submitting the form..</div>
) || (
<div><!-- Form elements in here --></div>
)}
</div>
);
});
When you call successFunction using window.location the URL is redirected to your webpage too or another domain? Why it's important, React is a single page application(SPA) so if you wanna change a page in the same domain or webpage I recommend you use a router, React has react-router or react-router-dom.
React Router Dom

React Promise - confusion in coding

I am new to react, and trying to follow a tutorial of Promises. I merged the two files into one as below for convenience. However, I am totally lost as to how to display the desired images. I copied the last bit from another react application.
import React from 'react'
function DemoPromise2() {
function loadImage(url) {
return new Promise((resolve, reject) => {
let image = new Image();
image.onload = function () {
resolve(image);
};
image.onerror = function () {
let message = "Could not load image at " + url;
reject(new Error(message));
};
image.src = url;
});
}
// export default loadImage;
let addImg = (src) => {
let imgElement = document.createElement("img");
imgElement.src = src;
document.body.appendChild(imgElement);
};
Promise.all([
loadImage("images/img1.jpg"),
loadImage("images/img2.jpg"),
loadImage("images/img3.jpg"),
loadImage("images/img4.jpg"),
])
.then((images) => {
images.forEach((img) => addImg(img.src));
})
.catch((error) => {
// handle error later
});
return (
<div className="App">
Demo Promise2 -
<br />
???? Question :: how can I display images here??
</div>
);
}
export default DemoPromise2;
#Evert is correct, you need to load this into state to show the images.
TL;DR: Click Run code snippet below to see the code in action.
--
Longer Explanation:
useState will allow the data to be accessed with the component and allow it to be persisted within it. You either get the value with it's name or setValue to update it.
Ex:
const [myvalue, setMyvalue] = useState('defaultValue');
useEffect is use as another React but specifically for when state get modified and given that you give specific things to look for (or watch as an array)
Example Nothing To Watch:
useEffect(() => {
console.log('CODE IS RUN ONLY ONCE, Nothing to watch except when component loads');
}, []);
Example State To Watch:
const [value, setValue] = useState('watch this');
useEffect(() => {
console.log('RUN EACH TIME value is updated including first time set');
}, [value]);
Code:
// main.js
// for your code use: import React, { useState, useEffect } from 'react';
const { useState, useEffect } = React;
const App = () => {
// State / Props
const [images, setImages] = useState([]);
// Load Images Function
const loadImage = (url) => {
return new Promise((resolve, reject) => {
let image = new Image();
image.onload = () => {
resolve(image);
};
image.onerror = () => {
let message = `Could not load ${url}`;
reject(new Error(message));
};
image.src = url;
});
};
// Hook to use when the component is loaded and ready
// Equivalent to something like window.onload
useEffect(() => {
Promise.all([
loadImage('https://picsum.photos/seed/picsum/200/200'),
loadImage('https://picsum.photos/200/200')
])
.then((data) => {
setImages(data);
})
.catch((error) => console.log('ERROR', error));
}, []);
return <div><h1>My Images</h1><p>Images Loaded: {images.length}</p>{images.map((img, index) => <img key={`img-${index}`} src={img.getAttribute('src')} />)}</div>;
};
ReactDOM.render(<App />, document.querySelector('#root'));
<body>
<div id="root"></div>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<script type="text/babel" src="main.js"></script>
</body>
====== CLARIFICATIONS ======
Question:
const [images, setImages] = useState([]); ???????????? the above defines const 'images'. Where is 'images' value modified? where is it receiving values/images?
Answer:
Think of this part as destructuring from a variable that's an array, but you're getting the getter and setter from useState.
The same way you would destructure an array like:
const myArray = [1, 2];
const [firstValue, secondValue] = myArray;
// firstValue = 1
// secondValue = 2
But instead of just plain values, you're getting the equivalent of functions returned to get the value and set a new value.
Note that useState is defined by React.
With that in mind, images is updated (set) here:
.then((data) => {
setImages(data);
})
Question:
2. let image = new Image(); ???????? whats the purpose of this sttmt? 'let image = new image()' ??? such was used to set an instance of a class but why is it used for a function? ????????
Answer:
new Image() is a base function baked into the browser, and would be typically be used for creating a new <img> with some event handling, or potentially other custom functionality.
Technically you do not need to use new Image() here if you're just going to rely on the browser to load the images naturally.
In the case of your initial code, the functionality exists to do something when the image is done loading, or handle errors in a specific way, but there isn't really any code except for resolving a promise.
What you would do is something like:
image.onload = () => {
// Images is loaded send message to custom logging software
// OR Image is loaded which works like a tracking pixel sent a message
// OR Image is loaded, append it to DOM and perform a fade in animation
resolve(image);
};
Sometimes you would just use this function to handle ordering images in a sequential way, or control the sequence of loading and animations.
On in the case of your code, wait until all the images are loaded, then show the page vs. loading them one by one natively in the browser.
Question:
3. image.onload = () => {resolve(image); 'image.onload' is just a const , will it hold the 'image'?
Answer:
It does not hold the image, this is just an event function that is defined by Image to handle when the image is done loading. So it's a predefined function that is called by Image, but defined by you.
Question:
?4. image.src = url .. ??
Answer:
This is the part of Image that starts the whole process, that's why it's defined at the end.
The code is saying.
// Before you begin you're going to be getting an image
let image = new Image();
// Before we load the image, I want to tell you how to handle it once
// it loads successfully
image.onload = function {
// custom instructions
};
// Before we load the image, I want to tell you how to handle it
// in case things go wrong
image.onerror = function {
// custom instructions
}
// Ok, we're good to go, start the process, here's the url
image.src = url; // https://picsum.photos/seed/picsum/200/200

Embed Pinterest widget in React

I wrote the following component to embed a Pinterest widget in my React app:
import React, { Component } from 'react'
class PinterestWidget extends Component {
componentWillMount() {
const script = document.createElement('script')
script.async = true
script.type = 'text/javascript'
script['data-pin-build'] = 'doBuild'
script.src = '//assets.pinterest.com/js/pinit.js'
document.body.appendChild(script)
}
render() {
const { url } = this.props
return (
<a data-pin-do="embedPin" data-pin-build="doBuild" href={url}>
{url}
</a>
)
}
}
export default PinterestWidget
It works fine when the component mount the first time.
But when I navigate to another route and go back to this page, it doesn't render the widget anymore.
I tried to place the script in componentDidUpdate, but no success.
PS: I searched for a component but the only one I found is react-pinterest which is not maintained for 2 years — and doesn't work in my app.
Can anybody help me?
I found myself in the exact same position today. After wrestling with forks of that same react-pinterest npm package I found a solution in this issue https://github.com/pinterest/widgets/issues/13.
It looks like you already got close by adding the data-pin-build="doBuild" data attribute but you aren't calling doBuild() when the user returns to the component after hitting a different route. Adding the data attribute instructs pinit.js to add doBuild() to the global scope but doesn't automatically call the function when you remount the component.
Try replacing your componentWillMount function with something like this:
componentDidMount() {
if (!window.doBuild) {
this.preloadWidgetScript();
} else {
window.doBuild();
}
}
preloadWidgetScript = () => {
const script = document.createElement('script');
script.async = true;
script.dataset.pinBuild = 'doBuild';
script.src = '//assets.pinterest.com/js/pinit.js';
document.body.appendChild(script);
}
One other thing to note is the change from componentWillMount to componentDidMount, this is necessary to ensure the embed link is rendered before running doBuild().
In my case, using the answer of #n33kos it works with useEffect and so far every time (without checking whether it has appended the script or not). Here the <a> link to pinterest exist in a markdown file that is injected as html
useEffect(() => {
const script = document.createElement('script')
script.async = true
script.type = 'text/javascript'
script.dataset.pinBuild = 'doBuild'
script.src = '//assets.pinterest.com/js/pinit.js'
document.body.appendChild(script)
if (window.doBuild) window.doBuild()
}, []) // only run once

Resources