如果组件卸载,则在React组件中停止异步功能

时间:2020-10-21 16:55:27

标签: javascript

说我有一个具有某些异步功能的React组件,例如setTimeOut函数,该函数在该组件的DOM元素上执行任务。如果在该setTimeOut期间卸载了组件,如何防止该异步函数触发?由于组件卸载导致DOM元素不再存在,因此触发的结果函数将引发错误。

由于我有一个非常具体但很少见的用例,在上一个问题中基本上没有任何见解,因此我试图提出一个有关此问题的理论方法的一般性问题。我所用的特定用例涉及动画完成后有时在动画过程中卸载了destroy()图形组件上的pixi-react-fiber图形组件。我认为这个问题的一般形式应该可以帮助我理解一种方法。

编辑:添加示例

Container.js

const CursorContainer = props => {
  const [darkMode, setDarkMode] = useState(props.darkMode)

  useEffect(() => {
    setDarkMode(props.darkMode)
  }, [props.darkMode])

  return (
    <Cursor darkMode={darkMode} />
  )
}

export default CursorContainer

Cursor.js

const TYPE = "Cursor"

export const behavior = {
  customDisplayObject: props => new PIXI.Graphics(),
  customApplyProps: function (g, _, props) {
    const { darkMode } = props
    g.clear()
    g.beginFill(darkMode ? 0x7225e6 : 0xd3fc03, 1)
    g.drawCircle(0, 0, 20)
    g.endFill()
    setTimeout(() => g.destroy(), 5000)
  },
}

const Cursor = CustomPIXIComponent(behavior, TYPE)

export default Cursor

如果切换darkMode,由于状态更改,Cursor组件将被卸载并重新安装。如果在5秒钟setTimeOut内发生这种情况,它将抛出一个错误,因为该组件的graphics DOM元素将消失,并且没有任何破坏作用。如果已卸载组件,如何防止触发此功能?

4 个答案:

答案 0 :(得分:1)

使用ref来维持对间隔的引用,然后在效果清理回调中清除间隔-

例如

const Component = () => {
    const intervalRef = useRef(null);
    
    useEffect(() => {
        intervalRef.current = setInterval(() => {}, 1000);
        
        return () => {
            clearInterval(intervalRef.current);
        }
    }, [])
}

使用ref将使您清除组件中其他处理程序的超时/时间间隔

编辑...

根据您的回复,如果要更新以下内容,则返回超时-

export const behavior = {
  customDisplayObject: props => new PIXI.Graphics(),
  customApplyProps: function (g, _, props) {
    const { darkMode } = props
    g.clear()
    g.beginFill(darkMode ? 0x7225e6 : 0xd3fc03, 1)
    g.drawCircle(0, 0, 20)
    g.endFill()
    return setTimeout(() => g.destroy(), 5000)
  },
}

然后在Cursor组件中的任何位置,只要您有权访问customApplyProps,您都可以从其中获取setTimeout引用,例如

intervalRef.current = customApplyProps();

然后在效果中,在返回回调中将其清除-

useEffect(() => {
    return () => {
        clearInterval(intervalRef.current);
    }
}, [])

答案 1 :(得分:0)

如果您使用的是功能组件,则可以在useEffect挂钩中返回一个清理函数,该组件在卸载时会自动调用。您可以使用clearTimeout返回的setTimeout函数,并在该清理函数中调用它。

  useEffect(() => {
    const timeout = setTimeout(..., ...);

    return () => {
      clearTimeout(timeout);
    };
  }, []);

答案 2 :(得分:0)

我认为这里更简单的方法是将功能提升到不会卸载的父组件。然后,您可以在父级中有一个状态来控制是否呈现子级组件,并在调用函数时将该状态用作逻辑门。

答案 3 :(得分:0)

我有一个简单的情景,这就是我的解决方法:

const CursorContainer = props => {

  const [darkMode, setDarkMode] = useState(props.darkMode)

  useEffect(() => {
   let mounted= true; 
   if(mounted) setDarkMode(props.darkMode)
    return () => { mounted=false ; }
  }, [props.darkMode])

  return (
    <Cursor darkMode={darkMode} />
  )
}

export default CursorContainer