React窗口resize时同步两个div高度触发无限循环报错如何解决
问题根源
- 无限循环的直接原因是你把
dimensions作为了修改它本身的useEffect的依赖项:useEffect内调用setDimensions会更新dimensions状态,状态更新又会重新触发useEffect执行,形成死循环。 - 原有逻辑没有监听窗口
resize事件,窗口大小变化时不会自动重新计算两个div的高度,没法做到实时同步。 - useRef的current属性变化不会触发依赖ref的useEffect重新执行,就算窗口大小变化导致左侧Card高度变了,原逻辑也感知不到。
解决方案
步骤1:封装正确的监听元素尺寸的自定义hook
这个hook会监听窗口resize事件,实时更新目标元素的尺寸,加防抖优化性能,同时避免循环依赖:
const useRefDimensions = (ref) => { const [dimensions, setDimensions] = useState({ width: 1, height: 2 }) useEffect(() => { if (!ref.current) return // 防抖函数,避免resize频繁触发更新 let timer = null const updateDimensions = () => { clearTimeout(timer) timer = setTimeout(() => { const boundingRect = ref.current.getBoundingClientRect() const { width, height } = boundingRect setDimensions({ width: Math.round(width), height: Math.round(height) + 35 }) }, 100) } // 初始化时执行一次 updateDimensions() // 监听窗口resize事件 window.addEventListener('resize', updateDimensions) // 卸载时清理事件和定时器 return () => { clearTimeout(timer) window.removeEventListener('resize', updateDimensions) } }, [ref]) // ref本身引用稳定,不会触发重复执行 return dimensions }
步骤2:修改Home组件逻辑
删掉原来组件内部写的有问题的useEffect,直接调用上面封装的自定义hook即可,渲染逻辑和原有代码完全一致:
import React, {useState, useEffect, useRef} from 'react' import { Card, Row, Col } from 'react-bootstrap' import PerfectScrollbar from 'react-perfect-scrollbar' import succession from '../../assets/images/succession/succession.jpeg' import successiontrailerposter from '../../assets/images/succession/succesion trailer.jpg' import successiontrailer from '../../assets/images/succession/successionTrailer.mp4' import {ReactVideo} from 'reactjs-media' const metadata = [ { title: "Succession", source: "HBO", type: "Original", mediatype: "TV Series", rating: "TV-MA", seasons: 3, episodes: 30, poster: succession, trailerposter: successiontrailerposter, trailer: successiontrailer, summary: "When Logan Roy (Brian Cox) announces his plans to step back from running the family's multi-national entertainment company, a feud over who will be have control causes tension among his four children.", info: [ { releasedate: "June 3, 2018", availabilitydate: "May 27, 2020", runtime: "56 - 70 minutes", genres:[ "Drama", "Comedy-Drama", "Black-Comedy" ], tags: ["Patriarchy", "finance", "based on true story" ], regions: ["North America", "Latin America"], language: "English", awards: ["Golden Globe: Best TV Series"], prod_budget: "$50,000,000", marketing_budget: "$3,000,000", } ] } ] // 上面封装的useRefDimensions hook放这里 const useRefDimensions = (ref) => { const [dimensions, setDimensions] = useState({ width: 1, height: 2 }) useEffect(() => { if (!ref.current) return let timer = null const updateDimensions = () => { clearTimeout(timer) timer = setTimeout(() => { const boundingRect = ref.current.getBoundingClientRect() const { width, height } = boundingRect setDimensions({ width: Math.round(width), height: Math.round(height) + 35 }) }, 100) } updateDimensions() window.addEventListener('resize', updateDimensions) return () => { clearTimeout(timer) window.removeEventListener('resize', updateDimensions) } }, [ref]) return dimensions } const Home = () => { const divRef = useRef() // 直接调用自定义hook获取尺寸 const dimensions = useRefDimensions(divRef) return ( <div className="container-fluid pt-3 px-5"> {metadata.map((item, index) => { return ( <div key={index}> <div className="d-flex align-items-end justify-content-between pb-4"> <h1 className="item-title m-0 text-primary text-bold">{item.title}</h1> <div className="d-flex flex-column align-items-end justify-content-end"> <p className="m-0">{item.source} {item.type} | {item.mediatype} | {item.rating}</p> <p className="m-0">Seasons: {item.seasons} ({item.episodes} episodes)</p> </div> </div> <Row> <Col xs={12} md={3} lg={2}> <Card bg="dark"> <Card.Img variant="top" src={item.poster} /> </Card> </Col> <Col xs={12} md={9} lg={10}> <Row className="media-info"> <Col md={12} lg={7} className="media-container" > <Card bg="dark" ref={divRef}> <ReactVideo src={item.trailer} poster={item.trailerposter} className="video-player"/> </Card> </Col> <Col md={12} lg={5} className="metadata-container" > <PerfectScrollbar style={{ height: dimensions.height }}> <Card bg="dark"> <Card.Body> <div className="mb-3"> <h4 className="card-h4">Series Summary</h4> <p className="card-p">{item.summary}</p> </div> <div> <h4 className="card-h4">Series Metadata</h4> <ul className="card-ul"> <li> <label>Release Date: </label> <span>{item.info[0].releasedate}</span> </li> <li> <label>HBO Max First Availability Date: </label> <span>{item.info[0].availabilitydate}</span> </li> <li> <label>Runtime: </label> <span>{item.info[0].runtime}</span> </li> <li> <label>Genre(s): </label> { item.info[0].genres.map((genre, index) => { return ( <span key={`genre_${index}`}> { (index ? ', ' : '') + genre } </span> ) })} </li> <li> <label>Plot Tag(s): </label> { item.info[0].tags.map((tag, index) => { return ( <span key={`tag_${index}`}> { (index ? ', ' : '') + tag } </span> ) })} </li> <li> <label>Region Availability: </label> { item.info[0].regions.map((region, index) => { return ( <span key={`region_${index}`}> { (index ? ', ' : '') + region } </span> ) })} </li> <li> <label>Original Language: </label> <span>{item.info[0].language}</span> </li> <li> <label>Awards: </label> { item.info[0].awards.map((award, index) => { return ( <span key={`award_${index}`}> { (index ? ', ' : '') + award } </span> ) })} </li> <li> <label>Production Budget: </label> <span>{item.info[0].prod_budget}</span> </li> <li> <label>Marketing Budget: </label> <span>{item.info[0].marketing_budget} </span> </li> </ul> </div> </Card.Body> </Card> </PerfectScrollbar> </Col> </Row> </Col> </Row> </div> ) })} </div> ) } export default Home
内容的提问来源于stack exchange,提问作者LOTUSMS
相关产品推荐
相关产品推荐

