无法在未安装的子组件上执行反应状态更新?

时间:2020-03-31 21:10:15

标签: reactjs react-redux react-state-management

正在收到此警告:

无法在未安装的组件上执行React状态更新。这是无人...

它是由child component引起的,我不知道如何消除它。

请注意,我已经阅读了许多其他文章,了解为什么会发生这种情况,并了解了基本问题。但是,大多数解决方案建议使用componentWillUnmount样式的功能取消订阅(我正在使用react hooks

我不知道这是否表明我对React有更大的基本误会,
但实际上这是我的认识:

import React, { useEffect, useRef } from 'react';
import Picker from 'emoji-picker-react';

const MyTextarea = (props) => {

  const onClick = (event, emojiObject) => {
    //do stuff...
  }

  const isMountedRef = useRef(true);

  useEffect(() => {
    isMountedRef.current = true;
  });

  useEffect(() => {
    return () => {
      console.log('will unmount');
      isMountedRef.current = false;
    }
  });

  return (
    <div>
      <textarea></textarea>
      { isMountedRef.current ? (
          <Picker onEmojiClick={onClick}/>
        ):null
      }
    </div>
  );
};

export default MyTextarea;

(tl; dr)请注意:

  • MyTextarea组件具有一个 parent 组件,该组件仅在特定路径上呈现。

  • 还有一个Menu组件,一旦单击该组件,即可根据情况更改route 或显示MyTextarea parent 组件或显示另一个组件。

  • 一旦我单击Menu关闭MyTextarea parent 组件,就会发生此警告。

更多上下文

  • StackOverflow 上的其他答案建议进行更改以防止在未安装组件时进行state更新。在我的情况下,我无法这样做,因为我没有设计Picker组件(由MyTextarea渲染)。 该警告来自此<Picker onEmojiClick={onClick}>行,但我不想修改此现成的组件。

  • 这说明了我基于isMountedRef渲染组件或不渲染组件的尝试。但是,这也不起作用。发生的情况是,如果我设置了useRef(true),则组件将被渲染;或者,如果我建议的设置为useRef(null),则组件将根本不会被渲染。

1 个答案:

答案 0 :(得分:1)

我不确定您的实际问题是什么(是您无法摆脱警告,还是<Picker>始终呈现或永不呈现),但是我将尽力解决我看到的所有问题。

首先,您不需要根据是否安装了<Picker>来有条件地渲染MyTextArea。由于组件仅在挂载后才呈现,因此<Picker>将永远不会在其所在的组件尚未挂载时呈现。

话虽如此,如果您仍然想跟踪组件何时安装,我建议您不要使用挂钩,而应将componentDidMountcomponentWillUnmountsetState()一起使用。这不仅使您更容易理解组件的生命周期,而且使用挂钩的方式也存在一些问题。

现在,初始化组件时,您的useRef(true)会将isMountedRef.current设置为true,因此即使在安装组件之前,它也将为true。 useRef()componentDidMount()不同。

在安装组件时,使用'useEffect()'将isMountedRef.current切换为true也不起作用。尽管将在安装组件时触发,但useEffect()的作用是副作用,而不是状态更新,因此它不会触发重新渲染,这就是为什么在设置useRef(null)时组件从不渲染的原因

此外,您的useEffect()钩子会在每次组件更新时触发,而不仅仅是在安装时触发,并且清理函数(返回的函数)也会在每次更新时触发,而不仅仅是在卸载时触发。因此,每次更新时,isMountedRef.current将从true切换为false,然后又切换为true。但是,这无关紧要,因为该组件无论如何都不会重新渲染(如上所述)。

如果确实需要使用useEffect(),则应将其组合为一个函数,并使用它来更新状态,以便触发重新渲染:

const [isMounted, setIsMounted] = useState(false);  // Create state variables

useEffect(() => {
  setIsMounted(true);  // The effect and clean up are in one function
  return () => {
    console.log('will unmount');
    setIsMounted(false);
  }
}, [] // This prevents firing on every update, w/o it you'll get an infinite loop
);

最后,从您共享的代码来看,您的组件无法引起警告,因为代码中的任何地方都没有状态更新。您应该检查选择器的存储库中是否有问题。

编辑:似乎该警告是由您的Picker软件包引起的,https://github.com/ealush/emoji-picker-react/issues/142已经存在问题