1

如果有应该播放的消息声音,我有一个useEffect()检查触发器布尔值的状态,并在播放后将活动消息触发器设置为 false 状态。

但是,useEffect() 进入了一个无限循环,导致应用程序崩溃。可能是因为更改状态会再次触发它(并且再次...)

通常,使用 useState,这很容易用类似的东西来修复useEffect(() => {logic}, [trigger])

就我而言,我没有使用 useState,但我正在使用减速器来更改状态。

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中使用时似乎没有修改状态,让我发布它的代码:

const reducer = (state, action) => {
  switch (action.type) {
    case "UPDATE_MESSAGES":
      return { ...state, messages: action.payload };
    default:
      throw new Error();
  }
};

export default reducer;
4

3 回答 3

2

尝试为您添加一个依赖项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);
于 2020-10-05T11:20:41.173 回答
1

我认为如果您将 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]);
于 2020-10-05T12:27:48.370 回答
1
// 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].

于 2020-10-05T13:44:19.413 回答