React.js - removeEvent() in useEffect() hook - javascript

I'm trying to get the HTML5 audio element to autoplay on page load. I know that Chrome doesn't allow autoplay by default, unless something triggers it. So I've created a couple of event listeners within the useEffect() hook:
const Audio = () => {
const audioPlayer = document.getElementById('myAudio')
const playAudio = () => audioPlayer.play()
useEffect(() => {
window.addEventListener('scroll', playAudio)
window.addEventListener('mousemove', playAudio)
return () => {
window.removeEventListener('scroll', playAudio)
window.removeEventListener('mousemove', playAudio)
}
}, [])
return (
<audio
id="myAudio"
src={ audioAssetHere }
/>
)
}
This does work but it keeps playing every time the cursor moves. I only want it to play once. I also get this error in the console:
DOMException: play() failed because the user didn't interact with the document first
And yet the audio still works every time the cursor moves.
I also tried using useRef and assign it to the audio tag ref={audioRef}, and then used it within the useEffect hook:
const audioRef = useRef()
const playAudio = () => audioRef.current.play()
useEffect(() => {
window.addEventListener('scroll', playAudio)
window.addEventListener('mousemove', playAudio)
return () => {
window.removeEventListener('scroll', playAudio)
window.removeEventListener('mousemove', playAudio)
}
}, [])
This gives the error:
TypeError: audioRef.current.play is not a function
So in a nutshell, what I want is the audio to play every time the page loads. And I only want it to play once.
Any help would be greatly appreciated. Thanks

you are trying to play audio on every mousemove/scroll event, instead of it you just need to play it once on the first mousemove/scroll event:
const Audio = () => {
const audioRef = React.useRef();
useEffect(() => {
const playAudio = () => {
audioRef.current.play();
removeListeners()
}
window.addEventListener('scroll', playAudio)
window.addEventListener('mousemove', playAudio)
const removeListeners = () => {
window.removeEventListener('scroll', playAudio)
window.removeEventListener('mousemove', playAudio)
}
return () => {
removeListeners()
}
}, [])
return (
<audio
src={ audioAssetHere }
ref={ audioRef }
/>
)
}

Related

Canceling a timeout in useEffect() if user scrolls with react hooks

Basically the same question as How to cancel a javascript function if the user scrolls the page but using react hooks.
I wrote react code that scrolls down to the end of the page after 3 seconds.
const scrollToEnd = () => { /* implementation omitted */ }
useEffect(() => {
const id = setTimeout(() => scrollToEnd(), 3000)
return () => clearTimeout(id)
}, [])
I want modify this code so that if the user manually scrolls the page before this timeout, the timeout is cleared.
I was thinking of a solution like:
const [hasScrolled, setHasScrolled] = useState(false);
const scrollToEnd = () => { /* implementation omitted */ }
useEffect(() => {
const setHasScrolledCallback = () => setHasScrolled(true)
window.addEventListener("scroll", setHasScrolledCallback);
return () => window.removeEventListener("scroll", setHasScrolledCallback);
}, []);
useEffect(() => {
const scrollCallback = () => { if (hasScrolled) scrollToEnd() }
const id = setTimeout(scrollCallback, 3000)
return () => clearTimeout(id)
}, [])
This works, but I don't think this is the correct way to approach this problem, because the scroll event is fired multiple times, even after the timeout occurs. Also the scrollCallback isn't really canceled, it runs anyway even if it does nothing.

Loop video with hover ReactJS

I am trying to autoplay a video when rendered, but only once. On the hover effect, I would like it to play the video with a loop. I have tried to use event.target.play() on mouseOver but have had no luck.
Any help would be greatly appreciated :)
<video
className="videos"
autoPlay={true}
onMouseOver={event => event.target.play()}
onMouseOut={event => event.target.pause()}
muted={true}
src={prompt}>
</video>
You can use onEnded event along with mouseover to loop it. Also using useRef makes it easy to set/play instead of passing event from all the callbacks. Check below code,
import { useEffect, useRef, useState } from "react";
import testVideo from "./testVideo.mp4";
export default function App() {
const ref = useRef(null);
const [focus, setFocus] = useState(false);
const loop = () => {
ref.current.play();
};
const onEndedLoop = () => {
if (focus) loop(); // when ended check if its focused then loop
};
useEffect(() => {
if (focus) loop(); // when focused then loop
}, [focus]);
return (
<div className="App">
<h2>Loop video with hover ReactJS!</h2>
<video
id="video"
ref={ref}
style={{ width: "300px" }}
autoPlay
onMouseOver={() => setFocus(true)}
onMouseOut={() => setFocus(false)}
muted={true}
src={testVideo}
onEnded={onEndedLoop}
></video>
</div>
);
}
check working demo - https://codesandbox.io/s/loop-video-on-hover-qegu7
#Madhuri has good answer (Which I upvoted).
However, if we add a function to pause loop when not focused, that will loop the video only when in focus and stop when not hovering over or not in focus.
const loop = () => {
ref.current.play();
};
const pauseLoop = () => {
ref.current.pause();
};
const onEndedLoop = () => {
if (focus) loop(); // when ended check if its focused then loop
};
useEffect(() => {
if (focus) loop(); // when focused then loop
if (!focus) pauseLoop(); // when not focused then pause loop
}, [focus]);

why i get different value width window using react js?

example:
resize using react js
this is my code:
import React, { useState, useEffect } from 'react';
const getWidthWindow = () => {
const [widthWindow, setWidthWindow] = useState(null)
const updateDimensions = () => {
setWidthWindow(window.screen.width)
}
useEffect(() => {
console.log(widthWindow)
setWidthWindow(window.screen.width)
updateDimensions()
window.addEventListener('resize', updateDimensions)
return () => window.removeEventListener('resize', updateDimensions)
}, [widthWindow])
}
export default getWidthWindow;
I want to get the window width value but the result is like it doesn't match the window size so how to fix it?
Your code is correct but the logging isn't.
Add a hook to log the dimensions when it updates:
useEffect(() => {
console.log(windowDimensions)
}, [windowDimensions])
Working codesandbox.
I go with the above answer of adding windowDimensions to the useEffect's Dependency array but I like to add up little sugar on top of it..
On Resize, the event gets triggered continuously and impacts performance a bit..
So, I have implemented throttling to improve the performance..
Answer for your updated question: Stackblitz link
const GetWidthWindow = () => {
const [widthWindow, setWidthWindow] = useState(window.innerWidth);
useEffect(() => {
let throttleResizeTimer = null;
function handleResize() {
clearTimeout(throttleResizeTimer);
throttleResizeTimer = setTimeout(
() => setWidthWindow(window.innerWidth),
500
);
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [widthWindow]);
return <p>{JSON.stringify(widthWindow)}</p>;
};
export default GetWidthWindow;
Answer for your old question:
useEffect(() => {
// implement throttle for little performance gain
let throttleResizeTimer = null;
function handleResize() {
clearTimeout(throttleResizeTimer);
throttleResizeTimer = setTimeout(
() => setWindowDimensions(getWindowDimensions()),
500
);
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize); }, [windowDimensions]);

useEffect not initiating audio file function

I'm trying to have a song play in the background of my app when it loads. However, it's not working with useEffect. I've also tried using useState in the useEffect with the same results. In the code below, if I click on the Play button, it works fine. Do you have any pointers on what I'm doing wrong?
First Attempt
const Footer = () => {
let audio = new Audio(onlyYou);
const start = () => {
audio.play();
};
const stop = () => {
audio.pause();
}
useEffect(() => {
start()
}, [])
return (
<footer>
<div>
<button onClick={start}>Play</button>
<button onClick={stop}>Stop</button>
</div>
</footer>
)
Second Attempt
const Footer = () => {
const [playSong, setPlaySong] = useState(false);
let audio = new Audio(onlyYou);
const start = () => {
audio.play();
};
const stop = () => {
audio.pause();
}
useEffect(() => {
setPlaySong(true)
}, [])
return (
<footer>
{playSong && start()}
<div>
<button onClick={start}>Play</button>
<button onClick={stop}>Stop</button>
</div>
</footer>
)
Try this
const audio = new Audio(onlyYou)
const Footer = () => {
useEffect(() => { audio.play() }, [])
return (...)
to see if it works. This has to work first. Otherwise you might run into the problem that audio can't be started without user confirm problem.
How to make audio autoplay on chrome
It may have to do with the fact that the audio file is not loaded and is not ready to play when useEffect runs. Take a look here.

addEventListener does not work within a useEffect hook

The following is a component whose functionality, partly, is to change the window's title as the page is getting focused and blurred. It does not work.
const ATitleChangingComponent = () => {
const focusFunction = (event: FocusEvent) => {
document.title = "focused";
};
const blurFunction = (event: FocusEvent) => {
document.title = "blurred";
};
useEffect(() => {
window.addEventListener("focus", focusFunction);
return window.removeEventListener("focus", focusFunction);
}, []);
useEffect(() => {
window.addEventListener("blur", blurFunction);
return window.removeEventListener("blur", blurFunction);
}, []);
return <p>some unimportant jsx</p>
};
However,
const focusFunction = (event: FocusEvent) => {
document.title = "focused";
};
window.addEventListener("focus", focusFunction);
works just fine.
A side question: are const focusFunction and const blurFunction getting constructed within the function each render? I assume if so, they should be lifted out of the component to avoid unnecessary overhead?
Need to return a function, otherwise listener is removed immediately.
The function gets called when the component unmounts
useEffect(() => {
window.addEventListener("blur", blurFunction);
return () => window.removeEventListener("blur", blurFunction);
}, []);

Categories