MutationObserver 正在读取旧的 React 状态

MutationObserver is reading old React State

我正在尝试将 MutationObserver 与 Zoom Web SDK 结合使用,以观察活动发言人的变化。我使用名为 participants 的 useState 声明了一个状态变量,它用于保存有关 Zoom 通话中每个参与者的信息。

我的 MutationObserver 似乎只读取参与者的初始值,这让我相信变量是 bound/evaluated 而不是动态读取。有没有办法将 MutationObserver 与 React useState 一起使用,以便 MutationCallback 读取动态更新的状态?

  const [participants, setParticipants] = useState({});

  ...

  const onSpeechMutation = (mutations) => {
    mutations.forEach((mutation) => {
      // identify name of speaker
      if(name in participants) {
        // do something
      } else {
        setParticipants({
        ...participants,
        [name] : initializeParticipant(name)
        })
      }
    })
  }

  ...

  useEffect(() => {
    if(!speechObserverOn) {
      setSpeechObserverOn(true);
      const speechObserver = new MutationObserver(onSpeechMutation);
      const speechConfig = {
        attributes: true,
        attributeOldValue: true,
        attributeFilter: ['class'],
        subtree: true,
      }
      const participantsList = document.querySelector('.participants-selector');
      if(participantsList) {
        speechObserver.observe(participantsList, speechConfig);
      }
    }
  }, [speechObserverOn])

如果您在回调中处理陈旧的状态封闭,那么通常解决方案是使用功能状态更新,这样您是从以前的状态更新,而不是在任何回调范围内关闭的状态。

const onSpeechMutation = (mutations) => {
  mutations.forEach((mutation) => {
    // identify name of speaker
    if (name in participants) {
      // do something
    } else {
      setParticipants(participants => ({
        ...participants, // <-- copy previous state
       [name]: initializeParticipant(name)
      }));
    }
  })
};

此外,确保为 useEffect 挂钩包含一个依赖项数组,除非您 真的 希望效果触发每个渲染周期。我猜你不想超过一个 MutationObserver at-a-time.

useEffect(() => {
  if(!speechObserverOn) {
    setSpeechObserverOn(true);
    const speechObserver = new MutationObserver(onSpeechMutation);
    const speechConfig = {
      attributes: true,
      attributeOldValue: true,
      attributeFilter: ['class'],
      subtree: true,
    }
    const participantsList = document.querySelector('.participants-selector');
    if(participantsList) {
      speechObserver.observe(participantsList, speechConfig);
    }
  }
}, []); // <-- empty dependency array to run once on component mount

更新

The issue is that if (name in participants) always returns false because participants is stale

为此,一个好技巧是使用 React ref 来缓存当前状态值的副本,以便任何回调都可以通过 ref 访问状态值。

示例:

const [participants, setParticipants] = useState([.....]);
const participantsRef = useRef(participants);

useEffect(() => {
  participantsRef.current = participants;
}, [participants]);

...

const onSpeechMutation = (mutations) => {
  mutations.forEach((mutation) => {
    // identify name of speaker
    if (name in participantsRef.current) {
      // do something
    } else {
      setParticipants(participants => ({
        ...participants,
       [name]: initializeParticipant(name)
      }));
    }
  })
};