使用减速器在 useEffect() 中反应无限循环
React infinite loop in useEffect() with reducer
我有一个 useEffect()
检查状态中的触发器布尔值是否有应该播放的消息声音,播放后它将活动消息触发器的状态设置为 false。
但是,useEffect() 进入无限循环,导致应用程序崩溃。可能是因为改变状态会再次触发它(又一次......)
通常,使用 useState,这很容易用 useEffect(() => {logic}, [trigger])
之类的东西修复
在我的例子中,我没有使用 useState,而是使用 reducer 来改变状态。
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.
让我给你看我评论的代码:
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;
如您所见,我没有使用 useState,而是使用了 reducer。据我所知,我经常发现的与以下内容相关的解决方案不是我的解决方案:
// Not applicable solution for me, since I use reducer
const [trigger] = useState();
useEffect(() => {
// Logic here
}, [trigger]);
编辑:由于reducer在useEffect中使用时似乎不会修改状态,让我post它的代码:
const reducer = (state, action) => {
switch (action.type) {
case "UPDATE_MESSAGES":
return { ...state, messages: action.payload };
default:
throw new Error();
}
};
export default reducer;
尝试为您的 useEffect
添加依赖项,例如:
useEffect(() => {
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;
}
props.dispatch({ type: "UPDATE_MESSAGES", payload: updatedMessagesState });
}
}, [activeMessage]);
通过不指定依赖项数组,您的 useEffect
将在每次渲染时 运行,从而创建一个无限循环。
此外,您正在尝试直接修改这一行的道具(并且它是反模式):
const messagesState = props.state.messages;
尝试将其更改为:
const messagesState = [...props.state.messages];
此外,let index = messagesState.indexOf(activeMessage);
将不起作用,因为 messagesState
是一个对象数组。要获取活动消息的索引,试试这个:
let index = messagesState.map(message => message.active).indexOf(true);
我想如果你添加 props.state.messages 作为依赖项,问题就会解决。此外,如果您仅在 useEffect 中使用 messagesState 和 messagesState,则应将此变量移至该块:
useEffect(() => {
const messagesState = props.state.messages;
const messagesState = 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 });
}
}, [props.state.messages]);
// 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 });
}
});
你的 useEffect 需要一个依赖项,如果你没有像你的情况那样在 useEffect 中提供依赖项,它总是 运行 在每个渲染上。在 useEffect 或 [any state or prop on which this effect depends]
.
中提供 []
作为第二个参数
我有一个 useEffect()
检查状态中的触发器布尔值是否有应该播放的消息声音,播放后它将活动消息触发器的状态设置为 false。
但是,useEffect() 进入无限循环,导致应用程序崩溃。可能是因为改变状态会再次触发它(又一次......)
通常,使用 useState,这很容易用 useEffect(() => {logic}, [trigger])
在我的例子中,我没有使用 useState,而是使用 reducer 来改变状态。
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.
让我给你看我评论的代码:
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;
如您所见,我没有使用 useState,而是使用了 reducer。据我所知,我经常发现的与以下内容相关的解决方案不是我的解决方案:
// Not applicable solution for me, since I use reducer
const [trigger] = useState();
useEffect(() => {
// Logic here
}, [trigger]);
编辑:由于reducer在useEffect中使用时似乎不会修改状态,让我post它的代码:
const reducer = (state, action) => {
switch (action.type) {
case "UPDATE_MESSAGES":
return { ...state, messages: action.payload };
default:
throw new Error();
}
};
export default reducer;
尝试为您的 useEffect
添加依赖项,例如:
useEffect(() => {
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;
}
props.dispatch({ type: "UPDATE_MESSAGES", payload: updatedMessagesState });
}
}, [activeMessage]);
通过不指定依赖项数组,您的 useEffect
将在每次渲染时 运行,从而创建一个无限循环。
此外,您正在尝试直接修改这一行的道具(并且它是反模式):
const messagesState = props.state.messages;
尝试将其更改为:
const messagesState = [...props.state.messages];
此外,let index = messagesState.indexOf(activeMessage);
将不起作用,因为 messagesState
是一个对象数组。要获取活动消息的索引,试试这个:
let index = messagesState.map(message => message.active).indexOf(true);
我想如果你添加 props.state.messages 作为依赖项,问题就会解决。此外,如果您仅在 useEffect 中使用 messagesState 和 messagesState,则应将此变量移至该块:
useEffect(() => {
const messagesState = props.state.messages;
const messagesState = 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 });
}
}, [props.state.messages]);
// 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 });
}
});
你的 useEffect 需要一个依赖项,如果你没有像你的情况那样在 useEffect 中提供依赖项,它总是 运行 在每个渲染上。在 useEffect 或 [any state or prop on which this effect depends]
.
[]
作为第二个参数