I have a useEffect()
that checks a trigger boolean in state if there is a message sound that should play, and after playing it sets that active message trigger to false in state.
However, the useEffect() goes into an infinite loop crashing the app. Probably because changing the state triggers it again (and again...)
Usually, with useState, this is fairly simple to fix with something like useEffect(() => {logic}, [trigger])
In my case I am not using useState, but I am using a reducer to change state.
Edit: The weird thing is, the reducer sometimes works to modify state, and sometimes it does not. It will execute without errors but the state remains unchanged.
Let me show you my commented code:
import React, { useEffect } from "react";
import { getCachedImage } from "../helpers";
const MessageNotification = (props) => {
const messageImg= getCachedImage("/ui/newmessage.png");
// Function that plays given sound
function playSound(soundFile) {
let audio = new Audio("/audio/messages/" + soundFile);
audio.play();
}
// Check if a Message is set to active. If so, execute logic
useEffect(() => {
// Get messages from state and find the message with "active" set to true
const messagesState = props.state.messages;
const activeMessage = messagesState.find((element) => element.active === true);
if (activeMessage) {
playSound(activeMessage.audio);
// Mark the message as userNotified and set it to inactive in state
let updatedMessagesState = messagesState ;
let index = messagesState.indexOf(activeMessage);
if (~index) {
updatedMessagesState[index].userNotified= true;
updatedMessagesState[index].active = false;
}
/* This is the weird part, the updatedMessagesState is correct,
but the dispatch reducer does not pass it to state.
This does work when I remove the useEffect
(but that gives me a fat red warning in console) */
props.dispatch({ type: "UPDATE_MESSAGES", payload: updatedMessagesState });
}
});
return (
<div>
<img src={"images" + messageImg} alt="message" width="90" height="90"></img>
</div>
);
};
export default MessageNotification;
As you can see, I do not use useState but work with a reducer instead. The solution that I often find which pertains to something like the following is not my solution as far as I can tell:
// Not applicable solution for me, since I use reducer
const [trigger] = useState();
useEffect(() => {
// Logic here
}, [trigger]);
Edit: Since the reducer does not seem to modify state when used in useEffect, let me post its code:
const reducer = (state, action) => {
switch (action.type) {
case "UPDATE_MESSAGES":
return { ...state, messages: action.payload };
default:
throw new Error();
}
};
export default reducer;
Try adding a dependency for your
useEffect
, such as:By not specifying a dependency array, your
useEffect
will run on EVERY render, hence creating an infinite loop.Also, you are trying to directly modify a prop (and it is an anti pattern) on this line:
Try changing it to this:
Also,
let index = messagesState.indexOf(activeMessage);
will not work sincemessagesState
is an object array. To get the index of the active message, try this: