useEffect for adding api getting recalled on user events
bradrice opened this issue · 5 comments
I'm having an issue with the useRef where I setup an api to call play after my source is ready. However, I also have a control on my page to increase or decrease the font size on the page, and for some reason the audio stops and restarts at the beginning. I've determned the useEffect is getting called again on that event. I cannot seem to figure out how to not call the useEffect and still get my player to play at the right time. If I add a dependencies array the useEffect does not start the player. If I remove them, the player starts properly, but then the events on the page cause it to be called again.
Here is my useEffect in my audio player using plyr usePlyr.
// Do all api access here, it is guaranteed to be called with the latest plyr instance
useEffect(() => {
const { current } = ref as React.MutableRefObject<APITypes>;
const api = current as { plyr: PlyrInstance };
console.log(
"useEffect in AudioPlayer",
isPlaying,
hasBookmarkChoice,
ref,
api
);
const onEnded = (e: any) => {
eventBus.dispatch("audioPlayEnd", { message: "audio ended" });
};
const onCanPlay = (e: any) => {
if (isPlaying && hasBookmarkChoice) {
console.log("Audio should be ready to play");
const promise = api.plyr.play();
if (promise) {
promise
.then(() => {
// put up an icon or do ui work for play started here
console.log("Play started");
})
.catch((error) => {
console.log("an error starting the audio occured", error);
});
}
} else {
console.log("Audio paused");
api.plyr.pause();
}
};
const onReady = (e: any) => {
console.log("I'm ready", e);
// api.plyr.play();
};
if (current.plyr.source === null) {
return;
} else {
api.plyr.on("ready", onReady);
api.plyr.on("canplay", onCanPlay);
api.plyr.on("ended", onEnded);
return () => {
api.plyr.off("ended", onEnded);
api.plyr.off("ready", onReady);
api.plyr.off("canplay", onCanPlay);
// clearTimeout(timeId);
};
}
});
Using 5.0.2
Could we store a variable (state, or something in the eventbus) and prevent the useEffect manually in some scenarios which are not desired. for example
const [hasBeenLoaded, setHasBeenLoaded] = useState()
useEffect(() => {
if (hasBeenLoaded) return;
// in some condition or code section call
// when you want to keep state of player the same for the rest of time.
setHasBeenLoaded(true)
})
You can also use the ref for it to prevent re-render
const hasBeenLoaded = useRef(false)
useEffect(() => {
if (hasBeenLoaded.current) return;
hasBeenLoaded.current = true;
})
Feel free to add the extra variables based on your need, and LMK if it solves your question or not
I tried both suggestions and nothing seems to work. It might be something in my own code causing the rerender, but if I view the html that is rendered by the code in the Dev Tools, I do see the div with class="class="plyr plyr--full-ui plyr--audio plyr--html5 plyr--playing" and wrapper div being redrawn regardless of anything above being re-rendered.
However, I have other onClick handlers on the page that don't cause a rerender when I click them. So I'm really having a hard time running this one down.
The usePlyr hook received third argument for its dep array (it is totally the same as useEffect one)
usePlyr.deps
Could you give it a shot to optimize the rendering and instantiation, separately from your events handler logics
Wow, after banging my head for weeks, that worked. I just passed an empty array as the third argument. Except, now the audio source doesn't get updated when I change pages. I'm not sure what I need to pass in the dependencies array.