簡體   English   中英

卸載組件后反應更新 state(內存泄漏)

[英]React updating state after component has been unmounted (memory leak)

我正在構建一個反應應用程序(我是 javascript 的菜鳥),並且在卸載組件后嘗試更新 state 並努力應對反應。

react 應用程序的上下文:作為學校項目的一部分,我必須為一個小型劇院綜合體構建電影票預訂服務,在 web 應用程序的主頁上,我有一個圖像“輪播”,每 5 秒更新一次圖像.

以下是實際操作中的主頁: https://apollo-9dd16.web.app/

(圖像加載時間只是因為我選擇了相當大的圖像,將被修復) - 首次訪問頁面時的加載屏幕也是因為從 firebase 加載所有內容需要很長時間(所有內容都通過 firebase 數據庫動態更新)如果有是一種讓它更快的方法請告訴我

此外,我已經瀏覽了盡可能多的 SO 帖子,每個人都建議在執行 state 更改之前添加檢查組件是否已安裝,我認為我已經完成了嗎?

無論如何,關於這個問題,由於某種原因,它會說我有一個 memory 泄漏,因為我試圖在卸載組件 state 后更改它(我無法輕松重現它,但它已經發生了很多次)

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

你非常接近:發生這種情況的原因是因為這段代碼:

/* 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 行(在上面的代碼段中)的if (mounted)條件有點太高了。 您應該將 if 語句包裝在第 4 行和第 10 行的setState調用周圍,因為它們都在setInterval function 回調中。

目前,代碼說:

  • 如果組件已安裝
  • 注冊此間隔,每 5 秒更新一次 state

但我相信你希望你的代碼說:

  • 如果組件已安裝
  • 每 5 秒更新一次 state,但前提是組件仍處於安裝狀態
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,
    };
  });
}

這應該在組件已經卸載后修復組件更新。 您甚至可以刪除上述代碼段開頭的if (mounted)檢查。

發生這種情況的原因是,在執行componentDidMount function 時, mounted變量為true ,因此它將注冊setInterval回調並每 5 秒執行一次。

雖然執行setInterval回調時mounted的變量可能不再為true ,但沒有什么可以阻止它執行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM