You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React拖拽拼图完成后组件未更新及后续内存泄漏问题求助

第一个问题(页面不更新)的原因

React类组件触发重新渲染的前提是state或props发生变更,你最初把Video和PuzzleCounter定义为类的普通实例属性,这类属性的变更不会被React监听,所以哪怕值修改成功,也不会触发render方法重新执行,页面自然不会更新。
解决方案就是你后续采用的:把需要触发视图更新的变量存入组件state,通过setState方法修改值,React会自动触发重新渲染。

第二个问题(内存泄漏警告)的解决方案

这个警告出现的原因是:组件已经被卸载后,代码还在执行setState操作。常见的触发场景是你注释的completeFunction执行后,父组件直接卸载了当前Game组件,后续的setState就变成了对已卸载组件的操作。
你可以按以下步骤修复:

  1. 增加组件挂载状态标记,避免对已卸载组件执行setState
  2. 把直接存JSX的实例属性改成渲染方法,避免作用域缓存导致的异常
    修改后的核心代码如下:
import React, { Component } from 'react'
import styled from 'styled-components';
import { DragDropContainer, DropTarget } from 'react-drag-drop-container';

import VideoMov from '../../videos/vid.mov'; 
import VideoWebm from '../../videos/vid2.webm';

import "./puzzle.css";

export class Game extends Component {
    // 增加挂载标记
    _isMounted = false;
    state = {
        video: false,
    }
    // 样式定义保持不变
    PuzzleContainer = styled.div`
        display: flex;
        flex-direction: column;
        margin-top: 50px;
    `;
        
    PuzzleBoard = styled.div`
        display: flex;
        align-items: center;
        justify-content: center;
        flex-direction: column;
    `;
    
    PuzzleBoardTop = styled.div`
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: center;
        margin-bottom: 60px;        
    `;

    PuzzleBoardPieces = styled.div`
        height: 50vw; 
        max-height: ${ ({ height }) => height ? height + "px" : "210px" };
        width: ${ ({ width }) => width ? width + "px" : "85px" }; 
        border: 1px solid #fff;
        background-color: ${ ({ color }) => color ? '#' + color : "transparent" };
        margin-right: ${ ({ margin }) => margin ? "60px" : "0px" }
    `;

    PuzzleBoardBottom = styled.div`
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: center;
        flex-wrap: wrap;
        width: 100%;    
    `;

    VideoWrapper = styled.div`
        width: 100%;
        height: 100%;
        margin-top: 50px;
        video {
            height: 100%;
            width: 100%;
        }
    `;
    
    PuzzleCounter = 0;

    // 生命周期管理挂载状态
    componentDidMount() {
        this._isMounted = true;
    }
    componentWillUnmount() {
        this._isMounted = false;
    }

    dropped = e => {
        e.containerElem.style.visibility = 'hidden';
        e.target.classList.add('highlighted');
        this.PuzzleCounter++;

        if (this.PuzzleCounter === 3) {
            // 先判断组件是否还挂载,再执行setState
            if(this._isMounted) {
                this.setState({ video :  true }, () => {
                    // setState回调里再执行父组件的完成方法,避免先卸载导致的警告
                    // this.props.completeFunction('complete');
                });
            }
        }
    }

    // 改成渲染方法,不要直接存JSX
    renderPuzzleGame = () => (
            <this.PuzzleBoard>
                <this.PuzzleBoardTop>
                    <DropTarget 
                        targetKey="piece-7314" 
                        dropData={ 
                            { name : 'left' }
                        }
                        onHit={ this.dropped }
                    >
                        <this.PuzzleBoardPieces className="puzzle-board left" />
                    </DropTarget>
                    <DropTarget 
                        targetKey="piece-3167" 
                        dropData={ 
                            { name : 'middle' }
                        }
                        onHit={ this.dropped }
                    >
                        <this.PuzzleBoardPieces className="puzzle-board middle" height={ 150 } width={ 90 } />
                    </DropTarget>
                    <DropTarget 
                        className="drop-container drop-container-right"
                        targetKey="piece-5149" 
                        dropData={ 
                            { name : 'right' }
                        }
                        onHit={ this.dropped }
                    >
                        <this.PuzzleBoardPieces className="puzzle-board right" />
                    </DropTarget>
                </this.PuzzleBoardTop>

                <this.PuzzleBoardBottom>
                    <DragDropContainer targetKey="foo">
                        <this.PuzzleBoardPieces className="puzzle-piece left" color="09203f" height={ 210 } width={ 170 } margin={ true } />
                    </DragDropContainer>
                    <DragDropContainer targetKey="foo">
                        <this.PuzzleBoardPieces className="puzzle-piece middle" color="0a4ba6" height={ 210 } width={ 90 } margin={ true } />
                    </DragDropContainer>
                    <DragDropContainer 
                        targetKey="piece-7314"
                        dragData={
                            { label : 'left' }
                        }
                    >
                        <this.PuzzleBoardPieces className="puzzle-piece left" color="118ab6" margin={ true } />
                    </DragDropContainer>
                    <DragDropContainer targetKey="foo">
                        <this.PuzzleBoardPieces className="puzzle-piece right" color="173966" height={ 215 } width={ 70 } margin={ true } />
                    </DragDropContainer>
                    <DragDropContainer 
                        targetKey="piece-3167" 
                        dragData={
                            { label : 'middle' }
                        }
                    >
                        <this.PuzzleBoardPieces className="puzzle-piece middle" color="052651" height={ 150 } width={ 90 } margin={ true } />
                    </DragDropContainer>
                    <DragDropContainer 
                        targetKey="piece-5149" 
                        dragData={
                            { label : 'right' }
                        }
                    >
                        <this.PuzzleBoardPieces className="puzzle-piece right" color="092c65" margin={ false } />
                    </DragDropContainer>
                </this.PuzzleBoardBottom>

            </this.PuzzleBoard>
    );

    renderVideoContainer = () => (
        <this.VideoWrapper>
            <video autoPlay muted playsInline>
                <source src={ VideoMov } type='video/mp4; codecs="hvc1"' />
                <source src={ VideoWebm } type="video/webm" />
            </video>
        </this.VideoWrapper>
    );

    render() {
        const { video } = this.state;
        return (
            <this.PuzzleContainer>
                { !video ? this.renderPuzzleGame() : this.renderVideoContainer() }
            </this.PuzzleContainer>
        );
    }
}

export default Game

如果修复后还有警告,可以检查react-drag-drop-container组件是否有未清理的事件绑定,按官方文档要求在卸载时销毁相关实例即可。

内容的提问来源于stack exchange,提问作者Thomas Hewitt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 13:45:03