React拖拽拼图完成后组件未更新及后续内存泄漏问题求助
第一个问题(页面不更新)的原因
React类组件触发重新渲染的前提是state或props发生变更,你最初把Video和PuzzleCounter定义为类的普通实例属性,这类属性的变更不会被React监听,所以哪怕值修改成功,也不会触发render方法重新执行,页面自然不会更新。
解决方案就是你后续采用的:把需要触发视图更新的变量存入组件state,通过setState方法修改值,React会自动触发重新渲染。
第二个问题(内存泄漏警告)的解决方案
这个警告出现的原因是:组件已经被卸载后,代码还在执行setState操作。常见的触发场景是你注释的completeFunction执行后,父组件直接卸载了当前Game组件,后续的setState就变成了对已卸载组件的操作。
你可以按以下步骤修复:
- 增加组件挂载状态标记,避免对已卸载组件执行
setState - 把直接存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
相关产品推荐
相关产品推荐

