How to detect if the React app is actually being viewed - reactjs

So I have created an app that shows data in realtime obtaining it from devices.
However, I want to make my server not obtain data when nobody is viewing the app.
So essentially I need some way to determine whether the app is currently being viewed, regardless of if it's desktop or mobile, this includes tab is on focus where the app is opened and that is what the user is currently viewing, and there is nothing on top of the browser, so browser opened on the correct tab, but user has explorer on top of it doing something entirely different, this for my case should be false, and for mobile, the same thing including if device is locked (screen off).
The reason for trying to do that, is to reduce the load on the devices, so that data is being requested, only when there is someone to view it.
From what I have researched I found out about the focus and blur events, but I was unable to make it work, and I don't even know if that is the correct approach, but what I have tried is:
Adding event listeners to the window in the App component:
function App() {
useEffect(() => {
window.addEventListener("focus", () => { console.log("viewed")});
window.addEventListener("blur", () => { console.log("hidden")});
})
}
Adding them as props to the App component within index.js:
<App onFocus={() => {console.log("viewed")}} onBlur={() => {console.log("hidden")}}/>
Neither had any kind of effect, I didn't get either of the console outputs.
Is that even the correct approach?

I would add a socket connection to the app. Then the server would be able to know if there are at least X persons connected and act accordingly.

I would suggest you to try socket for this kind of connection tested, but since you also wanna reduce the load for the user, testing if the user is focused in the browser is the way to go.
To achieve it, I won't add this code inside the React component because of the nature of React as all of its components are rendered inside the <div id="root"></div>, other parts of the html page will still be unaffected by this mechanism. So what you probably want to do is to add the code in the index.html and use window.userFocused to pass the value into React from the index
Edit: added focus/blur script
<script>
window.addEventListener("focus", function(event)
{
console.log("window is focused");
window.userFocused = true;
}, false);
window.addEventListener("blur", function(event)
{
console.log("window is blurred");
window.userFocused = false;
}, false);
</script>

So I ended up solving it with pretty much the same code as initiallywith a few slight modifications, I still used an useEffect hook:
const onFocusFunction = () => {
// do whatever when focus is gained
};
const onBlurFunction = () => {
// do whatever when focus is lost
};
useEffect(() => {
onFocusFunction();
window.addEventListener("focus", onFocusFunction);
window.addEventListener("blur", onBlurFunction);
return () => {
onBlurFunction();
window.removeEventListener("focus", onFocusFunction);
window.removeEventListener("blur", onBlurFunction);
};
}, []);

The best way is to use document.
document.onvisibilitychange = () => {
console.log(document.hidden)
}

Related

Adding Click Event Listener to Adobe Data Layer in React

I'm just starting to enter the world of Adobe Analytics. I'm working in React with Typescript and I am trying to leverage the adobe data layer to send information to Adobe Launch. I've been able to successfully use the adobe push function (i.e. window.adobeDataLayer.push({ test: 'test succeeded' })) and can both see that in the console via window.adobeDataLayer.getState() and ran a simple test to confirm it made its way to Adobe Launch.
However, when it comes to adding an event listener, I'm stumped. I attempted to follow Adobe's Documentation and came up with the following (doStuff() was just to confirm that eventListeners were working as expected, which they do):
function myHandler(event: any): void {
console.log("My handler was called")
}
function doStuff() {
console.log('do stuff was called')
}
function adobeAnalyticsTest(): void {
console.log(' adobeAnalyticsTest function called')
window.adobeDataLayer = window.adobeDataLayer || []
window.adobeDataLayer.push({ test: 'test succeeded' })
window.addEventListener('click', doStuff)
window.adobeDataLayer.push(function (dl: any) {
dl.getState()
dl.addEventListener('click', myHandler)
})
}
useEffect(() => {
adobeAnalyticsTest()
}, [])
Looking at window.adobeDataLayer, I couldn't see anything that seemed to indicate there was a click event listener (although this could be ignorance on my part) nor was 'My Handler was called' ever logged to the console. Does anyone have any ideas as to what I'm doing wrong or know how to tell when it's working correctly?
At face value, it looks like you don't have anything in your code that actually calls adobeAnalyticsTest().
Also, the listener you attach to dl doesn't listen for DOM events like clicking on something in window (like your window.addEventListener line); it listens for payloads pushed to adobeDataLayer where the passed event property value matches what you are listening for.
For example, put adobeDataLayer.push({'event':'click'}) in your js console you should see "My handler was called".
Think of it more like subscribing to a CustomEvent (because that's what it is, under the hood), rather than a native DOM event.

Limit for function that can run before browser goes to background (mobile browser)

I have code to handle visibilitychange like below
As far as I know, browser has fraction of time to run function before the browser's visibility become hidden.
Furthermore, the OS(android or iOS) can freely decide to stop the any running functions and put the browser into background.
Here is my simple code:
const Component = () => {
const [isHidden, setIsHidden] = useState(false)
useEffect( function whenHidden() => {
if(isHidden){
// when page visible === false,
// the order of execution:
// setIsHidden(true) > useEffect() > whenHidden() > anotherLongLogic() > reactDOM rerender
// considering this function is 3rd in order above, does this function guaranteed to run?
anotherLongLogic()
}
},[isHidden])
useEffect(() => {
window.addEventListener('visibilitychange', (e) => {
if(document.hidden){
// the order when hidden is
setIsHidden(true)
// how long does this function allow to run?
anotherLongLogic()
}
})
})
}
My Questions are (for mobile browser mainly):
with react, is it guaranteed that setState and useEffect will run within the limited time?
Is there any limit for the anotherLongLogic (how much time is allowed to run this function)?
can we block or tell the OS to give more time to browser to complete any functions that is still running?
any best practice to handle this case?

Prevent navigation until React form is validated

I'm trying to implement a validation functionality in React wherein the user will fill in a form, and then the user can navigate to other tabs of the application.
What i want to do is disabling the navigation until certain parts of the form has been filled, if the user attempts to change the tabs without filling said fields, a message will appear directly on the page to alert the user that the fields are required.
My current approach is to capture the event prior to the page switch, check for the form's validity, and then decide whether or not the user can navigate based on said.
The problem is I have no idea which event to capture or how to prevent user's navigation, can someone help me?
EDIT:
Here's what i've tried so far:
componentWillUnmount() {
this.handleValidation();
}
handleValidation = () => {
if (this.invalidForm()) {
this.setState({ showValidation: true });
this.preventNavigation();
}
};
preventNavigation = () => {
window.location.reload();
}
render()
return (
///Form information...
{this.state.showValidation && (
<div>
You must provide all required information
</div>
)}
)
I've thought that by using location.reload in componentWillUnmount(), the page would be reloaded before the navigation (hence, making the user stay in the old page), but what really happens is that it navigates to the other tab, then proceeds to reload there.

How does document.getSelection work under reactjs environment?

I was working on a electron-react project for epub file. Right now, I am planning to make the app capable of selecting text field and highlight it.
To achieve it, I was trying to use web's Window.getSelection api. However, there are some really weird things come up like in this.
In short, I am unable to capture the Selection object. It seems like even if I log the Selection object, this object could somehow jump to something else. Also, I cannot even serialize the Selection object with JSON.stringfy. This is super surprising, and this is my first time seeing something like this (I will get empty object to stringfy the Selection object).
So how to use Window.getSelection properly under react-electron environment? Or this api will not work well for text content which is generated by react's dangerouslySetInnerHTML?
Looks like the window.getSelection api needs to toString the selected object.
const getSelectionHandler = () => {
const selection = window.getSelection();
console.log("Got selection", selection.toString());
};
Super simple textarea and button to get selection react demo
this solution might help someone, catch last selection
const selection = useRef('');
const handleSelection = () => {
const text = document.getSelection().toString();
if (text) {
selection.current = text;
}
}
useEffect(() => {
document.addEventListener('selectionchange', handleSelection);
return () => {
document.removeEventListener('selectionchange', handleSelection);
};
});

How to capture document.ready or window.load events in Gatsby

I am relatively new to the Gatbsy framework and I am trying to figure out a way to toggle classes on some elements on DOMContentLoaded or on window.load, to animate them as soon as the user can see the screen.
This is what I did until now however it doesn't seem very appropriate:
componentDidMount = () => {
if (typeof window === "undefined") return
window.addEventListener("load", this.myEventHandler)
// or
if (typeof document === "undefined") return
document.addEventListener("DOMContentLoaded", this.myEventHandler)
}
Is there a better way of doing this?
Thank you in advance.
I think you should add these event listeners in the gatsby-browser.js:
// gatsby-browser.js
// ES6
export const onClientEntry = () => {
window.onload = () => { /* do stuff */ }
}
// or commonjs
exports.onClientEntry = () => {
window.onload = () => { /* do stuff */ }
}
You don't have to check for window there because this file is only run on the client side. Here's the full list of available hooks.
also AFAIK document doesn't have a ready event, it's a jQuery thing. You might be interested in DOMContentLoaded event, though there's some small different between that and jQuery's ready IIRC.
This is what I did until now however it doesn't seem very appropriate:
This is perfectly legitimate code imho:
componentDidMount = () => {
window.addEventListener("load", this.myEventHandler);
...
EDIT
Thinking about this, by the time componentDidMount has run, window "load" and document "ready" will already have fired... so it is a bit pointless.
https://codesandbox.io/s/n43z5x00j4
You can just use componentDidMount to check that the DOM has loaded and not bother with the other two events.

Resources