【问题标题】:React updating state after component has been unmounted (memory leak)卸载组件后反应更新状态(内存泄漏)
【发布时间】:2023-03-19 23:14:01
【问题描述】:

我正在构建一个 react 应用程序(我是 javascript 的菜鸟),并且在组件卸载后尝试更新状态。

react 应用程序的上下文:作为学校项目的一部分,我必须为一个小型剧院综合体构建一个电影票预订服务,在网络应用程序的主页上,我有一个图像“轮播”,它正在更新图像每 5 秒一次。

这里是主页:https://apollo-9dd16.web.app/

(图像加载时间只是因为我选择了相当大的图像,将被修复) - 首次访问页面时的加载屏幕也是因为从firebase加载所有内容需要很长时间(所有内容都通过firebase数据库动态更新) ) 如果有办法让它更快,请告诉我????

另外,我浏览了尽可能多的 SO 帖子,每个人都建议在执行状态更改之前添加一个检查组件是否已安装,我想我已经完成了吗?

无论如何,关于这个问题,由于某种原因,它会说我有内存泄漏,因为我试图在卸载后更改组件状态(我无法轻松重现它,但它已经发生了一些次)

import React, {Component} from 'react'
import { Link } from 'react-router-dom'
import MoviesContext from '../contexts/Movies'


/* Todo: 

 - Work on the transition between movies
 - Work on sending the user to the right place on book now button press

*/

class Home extends Component {
    static contextType = MoviesContext
    state = {
        intervalID:0,
        index:1,
        prevIndex:0,
        nextIndex:2,
        picList:[]
    }
    componentDidMount() {
        let mounted = true
        const posterURLS = [] // eslint-disable-next-line
        this.context.map((movies) => {
            posterURLS.push(movies.posterURL)
        })
        this.setState({picList: posterURLS})

        if(mounted) {
            const newIntervalId = setInterval(() => {
                if (this.state.nextIndex + 1 === this.state.picList.length ){
                    this.setState({ 
                        prevIndex: this.state.index,
                        index: this.state.nextIndex,
                        nextIndex: 0
                    })
                } else {
                    this.setState({
                        prevIndex: this.state.index,
                        index: this.state.nextIndex,
                        nextIndex: this.state.nextIndex + 1
                    })
                }
            }, 5000);
        
        
            this.setState(prevState => {
                return {
                    ...prevState,
                    intervalId: newIntervalId,
                };
            });

        }
        return () => mounted = false
    }
    render() {
        return (
            <div className="font-sans font-light text-theme-white text-4xl z-10 flex justify-center items-center h-screen">
                <div className="h-3/4">
                    <div className="h-full justify-center items-center">
                        <div className="h-full hidden md:flex">
                            <img src={this.state.picList[this.state.prevIndex]} alt="this is a movie" className="h-full rounded-2xl -mx-20"/>
                            <img src={this.state.picList[this.state.nextIndex]} alt="this is a movie" className="h-full rounded-2xl -mx-20"/>
                        </div>
                        <div className="absolute inset-10 flex justify-center items-center h-screen">
                            <img src={this.state.picList[this.state.index]} alt="this is a movie" className="h-3/4 rounded-2xl"/>
                        </div>
                    </div>
                    <div className="absolute inset-0 top-10 flex justify-center items-center">
                        <Link to="/book" className="bg-theme-light text-theme-black rounded-2xl py-3 px-5 hover:bg-theme-black hover:text-theme-light">Book Now</Link>
                    </div>
                </div>
                
            </div>  
        )  
    }

}

export default Home

【问题讨论】:

    标签: javascript reactjs jsx


    【解决方案1】:

    你已经很接近了!发生这种情况的原因是因为这段代码:

    /* 1 */  if (mounted) {
    /* 2 */    const newIntervalId = setInterval(() => {
    /* 3 */      if (this.state.nextIndex + 1 === this.state.picList.length) {
    /* 4 */        this.setState({
    /* 5 */          prevIndex: this.state.index,
    /* 6 */          index: this.state.nextIndex,
    /* 7 */          nextIndex: 0,
    /* 8 */        });
    /* 9 */      } else {
    /* 10 */        this.setState({
    /* 11 */          prevIndex: this.state.index,
    /* 12 */          index: this.state.nextIndex,
    /* 13 */          nextIndex: this.state.nextIndex + 1,
    /* 14 */        });
    /* 15 */      }
    /* 16 */    }, 5000);
    /* 17 */  
    /* 18 */    this.setState((prevState) => {
    /* 19 */      return {
    /* 20 */        ...prevState,
    /* 21 */        intervalId: newIntervalId,
    /* 22 */      };
    /* 23 */    });
    /* 24 */  }
    /* 25 */  
    

    第 1 行(在上面的 sn-p 中)上的 if (mounted) 条件有点太高了。您应该将 if 语句包裹在第 4 行和第 10 行的 setState 调用周围,因为它们都在 setInterval 函数回调中。

    目前,代码是这样说的:

    • 如果组件已安装
    • 注册此间隔,每 5 秒更新一次状态

    但我相信你希望你的代码说:

    • 如果组件已安装
    • 每 5 秒更新一次状态,但前提是组件仍处于挂载状态
    if (mounted) {
      const newIntervalId = setInterval(() => {
        if (this.state.nextIndex + 1 === this.state.picList.length) {
          if (mounted) {
            this.setState({
              prevIndex: this.state.index,
              index: this.state.nextIndex,
              nextIndex: 0,
            });
          }
        } else {
          if (mounted) {
            this.setState({
              prevIndex: this.state.index,
              index: this.state.nextIndex,
              nextIndex: this.state.nextIndex + 1,
            });
          }
        }
      }, 5000);
    
      this.setState((prevState) => {
        return {
          ...prevState,
          intervalId: newIntervalId,
        };
      });
    }
    
    

    这应该会在组件已卸载后修复组件更新。您甚至可以删除上述 sn-p 开头的 if (mounted) 检查。

    发生这种情况的原因是,在执行 componentDidMount 函数时,mounted 变量是 true,因此它会注册 setInterval 回调并每 5 秒执行一次。

    虽然mounted 变量在执行setInterval 回调时可能不再是true,但没有什么可以阻止它执行。

    【讨论】:

    • 非常感谢!我无法立即检查它,因为我无法让它可靠地发生,但是我会更新代码并希望它不会再次发生:)
    • 另外,我是否需要在最后一个 setState 周围设置一个 if(mounted)(更改间隔 id 的那个?)
    • 这不是必需的,因为我们知道当时(当componentDidMount 被调用时)setState 可以毫无问题地执行(组件刚刚挂载,因此我们可以安全地更新州)。
    猜你喜欢
    • 2017-10-24
    • 2020-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多