V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
suSouth
V2EX  ›  分享发现

React16.7 hooks 之 setTimeout 引发的 bug

  •  
  •   suSouth · 2018-12-13 15:19:30 +08:00 · 1107 次点击
    这是一个创建于 1954 天前的主题,其中的信息可能已经有所发展或是发生改变。

    React16.7 hooks 之 setTimeout 引发的 bug

    前言

    周末尝试了一下 React 新的 hooks 功能,来封装一个组件,遇到一个 bug,所以记录一下过程!

    报错如下:

    Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.in Notification

    大概意思是组件已经卸载了,但在卸载之后还执行了一个对组件更新的操作,这是一个无效的操作,但它表示应用程序中存在内存泄漏。要修复,请取消 useEffect cleanup function.in Notification 中的所有订阅和异步任务

    Can't perform a React state update on an unmounted component.,Read the Motivation to learn why we ’ re introducing Hooks to React

    组件核心代码如下:

    
    function Notification(props){
      var timer = null;
      const [visible, setVisible] = useState(false);
      let {title,description,duration,theme,onClose,}= props;
      let leave = (source='') => {
        clearTimeout(timer);
        setVisible(false);
        console.log("注意这里是 leave 方法里,timer 的 id:"+timer,"事件的来源:",source);
        console.log("leave result:",timer);
        onClose&&onClose();
      }
      
      let enter = () => {
        setVisible(true);
        if( duration > 0 ){
          let timer = setTimeout(() => {
            console.log(`auto carried out`,timer) //timer Number Id 
            leave(`Time to`);
          }, duration*1000);
          console.log(`enter 方法里,timer 的 id:`,timer) //timer Number Id 
        }
      }
    
      useEffect(()=>{
        enter();
      },[])
    
      return (
        <div className={`${prefixCls}-notice`} style={{display:`${visible?'':'none'}`}}>
          {!!theme&&<p className={`${prefixCls}-notice-icon`}><Svg iconId={`svg-${theme}`} /></p>}
          <div className={`${prefixCls}-notice-content`}>
          ……//首席填坑官∙苏南的专栏 交流:912594095、公众号:honeyBadger8
          </div>
          <p className={`${prefixCls}-notice-colse`} title="关闭" onClick={()=>leave("手动点击的关闭")}><Svg/></p>
        </div>
      );
    };
    
    

    简单分析:

    • 首先useEffect方法,是 react 新增的,它是componentDidMountcomponentDidUpdatecomponentWillUnmount三个生命周期的合集,
    • 也就是之前的写法,上面三生命周期里会执行到的操作,useEffect 都会去做;
    enter、leave 方法
    • 很好理解,进场出场两函数,
    • 进场:加了个定时器,在 N 秒后执行出场即 leave 方法,这个逻辑是正常的,
    • 问题就出在手动执行leave,也就是 onclick 事件上,
    问题原因:
    • 其实就是在点击事件的时候,没有获取到 timer 的 id,导致了定时器没有清除掉; !!看图说话:

    React v16.7 "Hooks" - What to Expect

    解决思路:
    • 当然是看官方文档,hooks 对我来说也是个新玩意,不会~
    • 1、useEffect方法里 return 一个方法,它是可以在组件卸载时执行的,
    • 2、清除定时器它有自己的方式,const intervalRef = useRef();指定赋值后能同步更新,之前的 timer 手动执行没有拿到 timer 所以没有清除掉;

    React v16.7 "Hooks" - What to Expect

    参考链接:

    中文,英文的没有找到 文档英文的也补一下吧 react github 也有人提到这个问题,学习了

    完美解决:

    请取消 useEffect cleanup function.in Notification 中的所有订阅和异步任务

    
    function Notification(props){
      var timer = null;
      const [visible, setVisible] = useState(false);
      let {title,description,duration,theme,onClose,}= props;
      const intervalRef = useRef(null);
      let leave = (source='') => {
        clearTimeout(intervalRef.current);
        setVisible(false);
        console.log("leave result:",source,intervalRef);
        onClose&&onClose();
      }
      
      let enter = () => {
        setVisible(true);
        if( duration > 0 ){
          let id = setTimeout(() => {
            console.log(`auto carried out`,intervalRef) //timer Number Id 
            leave(`Time to`);
          }, duration*1000);//首席填坑官∙苏南的专栏 交流:912594095、公众号:honeyBadger8
          intervalRef.current = id;
        }
      }
    
      useEffect(()=>{
        enter();
        return ()=>clearTimeout(intervalRef.current);
      },[])
    
      return (
        <div className={`${prefixCls}-notice`} style={{display:`${visible?'':'none'}`}}>
          {!!theme&&<p className={`${prefixCls}-notice-icon`}><Svg iconId={`svg-${theme}`} /></p>}
          <div className={`${prefixCls}-notice-content`}>
            ……//首席填坑官∙苏南的专栏 交流:912594095、公众号:honeyBadger8
          </div>
          <p className={`${prefixCls}-notice-colse`} title="关闭" onClick={()=>leave("手动点击的关闭")}><Svg/></p>
        </div>
      );
    };
    
    

    宝剑锋从磨砺出,梅花香自苦寒来,做有温度的攻城狮!,公众号:honeyBadger8

    热门推荐

    作者:苏南 - 首席填坑官 链接: https://blog.csdn.net/weixin_43254766/article/details/83758660 交流:912594095、公众号:honeyBadger8 本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5216 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 08:23 · PVG 16:23 · LAX 01:23 · JFK 04:23
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.