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

React useRef在useEffect中仍未设置,图片动态定位失效

问题说明

尝试通过useRef获取DOM元素的动态尺寸,用来将图片显示在随机位置,但初始获取useRef.current时返回undefined,推测是目标div尚未加载完成。用useEffect等待元素加载后,发现useRef.current的尺寸始终为0,导致图片无法正常移动。

原代码

import './MouseAim.css'
import '../Game.css'

import React, {useEffect, useRef, useState} from 'react'

import TargetImg from '../../../assets/Target.png'
import _ from "lodash";

function MouseAim() {
    const [start, setStart] = useState(true)
    const [target, setTarget] = useState(true)
    const elementDimensions = useRef()

    //wait for elementDimensions to be set
    useEffect(() => {
        console.log('elementDimensions', elementDimensions.current?.clientHeight)
    } , [elementDimensions])

    return (
        <div>
            <div className="main-container">
                {
                        start ?

                            <div ref={elementDimensions} className="aim-container">
                                {
                                    target ?
                                            <input 
                                                className="target" 
                                                type="image" 
                                                style={{position: "relative", left:elementDimensions.current?.clientHeight+"px" , top:_.random(0, elementDimensions.current?.clientHeight)+"px"}}
                                                onClick={() => console.log("hello")} 
                                                src={TargetImg} 
                                                alt="target"
                                            />
                                    :null
                                }
                            </div>

                        :
                            <div className="start-container">
                                <input className="start-button" type="button" value="Start Game" onClick={() => setStart(true)}/>
                            </div>
                }
            </div>
        </div>
    )
}

export default MouseAim

问题原因及修复方案

核心问题

  1. useRef不触发重渲染:elementDimensions是ref对象,其current属性变化不会触发组件重渲染,所以依赖elementDimensions的useEffect无法响应元素尺寸的真实变化。
  2. 尺寸计算时机错误:JSX渲染时直接读取elementDimensions.current?.clientHeight,初始阶段元素可能未完成CSS布局,导致值为0或undefined,且后续尺寸更新无法同步到样式。
  3. 定位方式不合理:使用relative定位时,left/top是相对于元素自身初始位置偏移,而非容器范围,导致位置计算逻辑错误。

修复代码

import './MouseAim.css'
import '../Game.css'

import React, { useEffect, useRef, useState } from 'react'
import TargetImg from '../../../assets/Target.png'
import _ from "lodash";

function MouseAim() {
    const [start, setStart] = useState(true)
    const [target, setTarget] = useState(true)
    const containerRef = useRef()
    const [containerSize, setContainerSize] = useState({ width: 0, height: 0 })
    const [targetPosition, setTargetPosition] = useState({ left: 0, top: 0 })

    // 监听容器尺寸变化,更新目标位置
    useEffect(() => {
        const updateContainerInfo = () => {
            if (!containerRef.current) return
            const { clientWidth, clientHeight } = containerRef.current
            setContainerSize({ width: clientWidth, height: clientHeight })
            // 计算目标位置时减去图片自身尺寸,避免超出容器(假设图片为50x50,可根据实际调整)
            setTargetPosition({
                left: _.random(0, clientWidth - 50),
                top: _.random(0, clientHeight - 50)
            })
        }

        // 初始加载时获取尺寸
        updateContainerInfo()

        // 监听窗口大小变化
        window.addEventListener('resize', updateContainerInfo)

        // 监听容器自身尺寸变化(比如CSS加载完成、容器内容变更)
        const resizeObserver = new ResizeObserver(updateContainerInfo)
        resizeObserver.observe(containerRef.current)

        // 清理监听
        return () => {
            window.removeEventListener('resize', updateContainerInfo)
            resizeObserver.disconnect()
        }
    }, [])

    // 点击目标后重新随机位置
    const handleTargetClick = () => {
        const { width, height } = containerSize
        setTargetPosition({
            left: _.random(0, width - 50),
            top: _.random(0, height - 50)
        })
    }

    return (
        <div>
            <div className="main-container">
                {start ? (
                    <div ref={containerRef} className="aim-container" style={{ position: 'relative' }}>
                        {target && (
                            <input
                                className="target"
                                type="image"
                                style={{
                                    position: "absolute",
                                    left: `${targetPosition.left}px`,
                                    top: `${targetPosition.top}px`
                                }}
                                onClick={handleTargetClick}
                                src={TargetImg}
                                alt="target"
                            />
                        )}
                    </div>
                ) : (
                    <div className="start-container">
                        <input
                            className="start-button"
                            type="button"
                            value="Start Game"
                            onClick={() => setStart(true)}
                        />
                    </div>
                )}
            </div>
        </div>
    )
}

export default MouseAim

关键修改说明

  • 用containerSize状态存储容器尺寸,确保尺寸变化能触发组件重渲染,同步更新目标位置。
  • 新增ResizeObserver和窗口resize事件监听,覆盖容器尺寸变化的所有场景。
  • 目标图片改用absolute定位,配合父容器的relative定位,实现基于容器范围的位置计算。
  • 点击目标时重新生成随机位置,增强交互逻辑。
  • 计算随机位置时减去图片自身尺寸,避免图片超出容器可视范围。

内容的提问来源于stack exchange,提问作者Thavas Antonio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 21:39:20