React 组件卸载怎么中止递归方法

问题的出现------组件卸载怎么中止递归方法

朋友在处理弹幕相关业务的时候,出现了一个bug:组件中的方法在组件卸载后仍然在执行。代码片段发给我看,但是变量的用意我也不懂,只看到有方法调用自身方法,这不就是递归嘛(最后发现是定时器没有清除的问题)🥲。

递归的中止这还不简单,在方法中添加一个中止条件不就好了。

tsx 复制代码
const Children = () => {
  const [isStop, setStop] = useState(false)
  
  // 递归
  const recursion = async (num: number) => {
    if (isStop) return
    console.log(num)
    await sleep(1000)
    recursion(num + 1)
  }
  
  useEffect(() => {
    recursion(1)
    return () => {
      setStop(() => true)
    }
  }, [])
  
  return (
    <div>Children</div>
  )
}

const HomePage: React.FC = () => {
  const [flag, setFlag] = useState(false)
  return (
    <div>
      <Button onClick={() => setFlag(!flag)}>点击</Button>
      {
        flag && <Children></Children>
      }
    </div>
  );
};

结果与预期好像不太一样,突然仔细想一下,当组件卸载时,所有与该组件相关的状态(通过useState定义的状态)都会被清除,这怎么可能会有效呢!

简单闭包处理

既然状态在组件卸载的时候被清理了,那么在递归内部增加状态来控制递归不就行了。闭包就很适合现在的场景。

tsx 复制代码
  const controller = () => {
    // 递归的状态(开始/结束)
    let state = false
    // 递归方法
    const recursion = async (num: number) => {
      // 结束条件
      if (state === false) return
      console.log(num)
      await sleep(1000)
      recursion(num + 1)
    }
    return {
      // 开启递归
      start: (num: number) => {
        state = true
        recursion(num)
      },
      // 结束递归
      close: () => state = false
    }
  }

我们在挂载结束后创建一个单一实例用来控制递归方法,并在卸载的时候将递归的状态改为false即可。如果需要在组件的其他位置使用,还是使用useRef对实例进行包裹比较好。

tsx 复制代码
  useEffect(() => {
    let c = controller()
    c.start(1)
    return () => {
      c.close()
    }
  }, [])

Hook useRef处理

tsx 复制代码
const Children = () => {
  const isUnmounted = useRef(false);
  
  // 递归
  const recursion = async (num: number) => {
    if (isUnmounted.current) return
    console.log(num)
    await sleep(1000)
    recursion(num + 1)
  }
  
  useEffect(() => {
    recursion(1)
    return () => {
      isUnmounted.current = true
    }
  }, [])
  
  return (
    <div>Children</div>
  )
}

const HomePage: React.FC = () => {
  const [flag, setFlag] = useState(false)
  return (
    <div>
      <Button onClick={() => setFlag(!flag)}>点击</Button>
      {
        flag && <Children></Children>
      }
    </div>
  );
};

来自CHAT-GPT


这是因为useRef创建的引用对象是在组件的闭包中存在的,而不是作为组件的状态存在。所以即使组件卸载,引用对象仍然可以被访问和修改。

个人理解,useRef创建的引用对象在递归方法中被引用,类似闭包的原理,所以在组件卸载的时候没有被清理。(如果理解错误,欢迎指正,轻点喷🫠)

用途

大家都是大佬,谁写递归没有中止条件呀 ,对吧😎。

我觉得在页面请求完数据之后,需要对数据进行复杂递归处理的时候,又把页面关闭,这种情况可能需要。

还有其他情况吗?欢迎大家补充哦!

相关推荐
console.log('npc')12 小时前
AI前端工程与生成式UI学习路线
前端·人工智能·ui
梦曦i13 小时前
uni-router v1.1.1发布:守卫超时保护+路由监听
前端·uni-app
qq_25183645713 小时前
基于java Web网络订餐系统设计与实现 源码文档
java·开发语言·前端
飞天狗11113 小时前
零基础JavaWeb入门——第2课:让网页“活”起来 —— JSP是什么?
java·开发语言·前端·后端·web
回忆2012初秋14 小时前
【Nginx】优雅地走进高性能 Web 服务器世界(1)
服务器·前端·nginx
kyriewen14 小时前
Claude Code Token 烧太快?实测 5 招,把月费从 250 美金砍到 50 美金
前端·ai编程·claude
weixin_3947580315 小时前
CRMEB Pro 商品字段二开:为什么加一个字段会牵动 SKU、缓存和前端展示?
前端·缓存
IT_陈寒15 小时前
Python的pickle让我半夜加班,这破玩意儿太坑了
前端·人工智能·后端
qq_4221525715 小时前
图片格式转换工具怎么选?JPEG、PNG、WebP、AVIF 格式对比与在线转换方案实测
前端