反应内存泄漏

时间:2020-01-10 15:52:23

标签: javascript reactjs memory

我有一个导致内存泄漏的组件。基本上是产品缩略图的网格。如果单击一个,则打开产品详细信息视图。 打开详细信息视图,让我们让chrome中的gpu进程消耗更多的内存。卸下组件,几乎没有任何余地。该应用程序可以非常快速地分配多个演出。 在Firefox中影响不那么大。开启费用约为4mb,卸载费用为2mb。 我不知道发生了什么事。我将非常感谢您提供一些提示和提示。

编辑:

嗨, 最初的答复。我制作了一个兼容codesandbox的版本。内存泄漏也适用于codeandbox标签。

codesandbox链接: https://codesandbox.io/s/confident-panini-6r9tp

这是产品的缩略图网格:

class Topic extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      currentProduct: null,
      productVisible: false,
      uiLocked: false
    };
    this.onSelectProduct = this.onSelectProduct.bind(this);
    this.onCloseProduct = this.onCloseProduct.bind(this);
    this.resetTimer = this.resetTimer.bind(this);
  }

  componentDidMount() {
    this.initializeTimer();
  }

  componentWillUnmount() {
    this.clearTimersAndListeners();
  }

  initializeTimer() {
    window.addEventListener("click", this.resetTimer);
    window.addEventListener("scroll", this.resetTimer, true);
    this.resetTimer();
  }

  clearTimersAndListeners() {
    window.removeEventListener("click", this.resetTimer);
    window.removeEventListener("scroll", this.resetTimer);
    if (this.timer) {
      window.clearTimeout(this.timer);
    }
  }

  resetTimer() {
    const { resetDelay } = this.context;
    if (this.timer) {
      window.clearTimeout(this.timer);
    }
    this.timer = window.setTimeout(this.onCloseProduct, resetDelay * 1000);
  }

  onSelectProduct(selectedProduct) {
    this.setState({
      currentProduct: selectedProduct.slug,
      productVisible: true
    });
  }
  onCloseProduct() {
    this.setState({ productVisible: false, uiLocked: true });
    setTimeout(
      () =>
        this.setState({
          currentProduct: null,
          uiLocked: false
        }),
      300
    );
  }
  render() {
    const context = this.context;

    const { topicSlug } = this.props.match.params;
    const { currentProduct, productVisible, uiLocked } = this.state;
    const topic = context.topics.find(topic => topic.slug === topicSlug);
    const { products, title, longtitle, subtitle, icon } = topic;

    const productObject = products.find(
      product => product.slug === currentProduct
    );

    const lang = context.language;
    return (
      <AppContext.Consumer>
        {context => {
          const { enableTopicNavigation } = context;
          return (
            <>
              <GlobalStyle locked={uiLocked} />
              <Frame
                title={title[lang]}
                backButtonVisible={productVisible}
                onNavigateBack={this.onCloseProduct}
                topicColor={topic.color}
                backToTopicNavigationLinkVisible={enableTopicNavigation && !currentProduct}
                isProduct={currentProduct}
              >
                <Wrapper topic={topic}>
                  <TopicHeader
                    icon={icon}
                    title={longtitle[lang]}
                    subtitle={subtitle ? subtitle[lang] : null}
                  />
                  <TileContainerScrollWrapper>
                    <TileContainer>
                      {products.map(product => (
                        <ProductTile
                          onClick={e => this.onSelectProduct(product)}
                          key={product.slug}
                          title={product.name[lang]}
                          image={product.thumbnail}
                          enlargeImage={product.enlargeThumbnail}
                          color={topic.color}
                        />
                      ))}
                    </TileContainer>
                  </TileContainerScrollWrapper>
                  {currentProduct && (
                    <Product
                      key={productObject.slug}
                      onClose={this.onCloseProduct}
                      product={productObject}
                      visible={productVisible}
                      topic={topic}
                    />
                  )}
                </Wrapper>
              </Frame>
            </>
          );
        }}
      </AppContext.Consumer>
    );
  }
}

Topic.contextType = AppContext;

export default Topic;

这是产品

class Product extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      mounted: false,
      mediaFullscreen: false,
      currentSlideIndex: 0
    };
    this.player = [];
    this.latestTap = new Date(0);
    this.toggleMediaFullScreen = this.toggleMediaFullScreen.bind(this);
    this.scrollToMedia = this.scrollToMedia.bind(this);
    this.checkForDoubleTap = this.checkForDoubleTap.bind(this);
  }

  componentDidMount() {
    console.log("mount")
    const { product } = this.props;
    setTimeout(
      () =>
        this.setState({
          mounted: true
        }),
      0
    );
    if (product.media) {
      this.embla.on("select", () => {
        const currentSlideIndex = this.embla.selectedScrollSnap();
        this.setState({ currentSlideIndex });
        this.player.forEach(currentPlayer => currentPlayer.handleStop());
        if(this.player[currentSlideIndex]) {
          this.player[currentSlideIndex].handlePlay();
        }
      });
    }
  }

  componentWillUnmount() {

  }

  scrollToMedia(index) {
    this.embla.scrollTo(index);
  }

  toggleMediaFullScreen() {
    this.setState({ mediaFullscreen: !this.state.mediaFullscreen });
  }

  checkForDoubleTap() {
    var now = new Date().getTime();
    var timeSince = now - this.latestTap;
    if (timeSince < 500 && timeSince > 0) {
      this.toggleMediaFullScreen();
    }
    this.latestTap = new Date().getTime();
  }

  render() {
    const { mounted, mediaFullscreen, currentSlideIndex } = this.state;
    const { product, visible, topic } = this.props;
    const componentIsVisible = mounted && visible;
    return (
      <AppContext.Consumer>
        {context => {
          const lang = context.language;
          const { i18n } = context;
          return (
            <>
              <GlobalStyle overrideBackground={componentIsVisible} />
              <Wrapper visible={componentIsVisible} color={topic.color}>
                {product.media && (
                  <>
                    <EmblaWrapper
                      fullscreen={mediaFullscreen}
                    >
                      <EmblaCarouselReact
                        emblaRef={c => (this.embla = c)}
                        options={{
                          loop: false,
                          containerSelector: ".slide-wrapper",
                          speed: 5
                        }}
                        style={{ }}
                      >
                        <SlideWrapper className="slide-wrapper">
                          {product.media.map((media, index) => {
                            const isCurrentSlide = index === currentSlideIndex;
                            return (
                              <MediaDetail
                                key={index}
                                fullscreen={mediaFullscreen}
                                onClick={this.checkForDoubleTap}
                              >
                                <CurrentMedia>
                                  {media.type === "image" && (
                                    <CurrentImage
                                      src={process.env.PUBLIC_URL + media.source}
                                    />
                                  )}

                                  {media.type === "video" && media.turntable && (
                                    <VideoPlayer
                                      source={process.env.PUBLIC_URL + media.source}
                                      thumbnail={media.thumbnail}
                                      interactable={false}
                                      fullscreen={mediaFullscreen}
                                      color={topic.color}
                                    />
                                  )}

                                  {media.type === "video" && !media.turntable && (                          
                                    <PlyrPlayer
                                      ref={x => {this.player[index] = x }}
                                      key={process.env.PUBLIC_URL + media.source}
                                      source={process.env.PUBLIC_URL + media.source}
                                      thumbnail={media.thumbnail}
                                      interactable={!media.turntable}
                                      inFullscreen={mediaFullscreen}
                                      color={topic.color}
                                      autoPlay={isCurrentSlide}
                                    />
                                  )}
                                </CurrentMedia>
                              </MediaDetail>
                            );
                          })}
                        </SlideWrapper>
                      </EmblaCarouselReact>
                    </EmblaWrapper>
                    <FullscreenButton
                      visible={!mediaFullscreen}
                      color={topic.color}
                      onClick={this.toggleMediaFullScreen}
                    >
                      <FullScreenIcon
                        src={
                          process.env.PUBLIC_URL +
                          "/images/icons/ico-full-screen.svg"
                        }
                      />
                      <FullScreenText>{i18n.fullscreen[lang]}</FullScreenText>
                    </FullscreenButton>

                    <FullscreenButton
                      visible={mediaFullscreen}
                      color={topic.color}
                      onClick={this.toggleMediaFullScreen}
                    >
                      <FullScreenIcon
                        src={
                          process.env.PUBLIC_URL + "/images/icons/ico-close.svg"
                        }
                      />
                      <FullScreenText>
                        {i18n.fullscreenClose[lang]}
                      </FullScreenText>
                    </FullscreenButton>
                  </>
                )}

                <MediaOverview visible={!mediaFullscreen}>
                  <ThumbnailGrid>
                    {"media" in product &&
                      product.media.length > 0 &&
                      product.media.map((media, index) => (
                        <MediaThumbnail
                          key={index}
                          media={media}
                          onClick={e => this.scrollToMedia(index, media)}
                        />
                      ))}
                  </ThumbnailGrid>
                </MediaOverview>
                <Overlay color={topic.color} visible={!mediaFullscreen}>
                  <ScrollWrapper>
                    <Scrollable>
                      <HeadingContainer>
                        <Heading>{product.name[lang]}</Heading>
                        <FormattedText
                          content={product.subtitle && product.subtitle[lang]}
                          wrapperElement={Subtitle}
                        />
                      </HeadingContainer>
                      {"content" in product &&
                        product.content.length > 0 &&
                        product.content.map((contentItem, index) => (
                          <OverlayContent key={index} content={contentItem} />
                        ))}
                    </Scrollable>
                  </ScrollWrapper>
                </Overlay>
              </Wrapper>
            </>
          );
        }}
      </AppContext.Consumer>
    );
  }
}

export default Product;

我剪切了样式化的组件并导入

0 个答案:

没有答案