React useRef即使在useEffect中也未设置



我正试图使用ref根据动态维度在随机位置显示图像。然而,当获取useRef的当前值时,我收到未定义的结果,可能是因为useRef目标div尚未加载。

为了克服这个问题,我使用useEffect来等待加载目标div。

但是,当我运行程序时,图像不会移动,因为useRef的当前值保持为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

只需将目标初始状态设置为"false",然后在"ref"准备好时将其设置为true

试试这个:

const [start, setStart] = useState(true)
const [target, setTarget] = useState(false)
const elementDimensions = useRef()

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

正如您所提到的,useRef最初是在div尚未装入时设置的。

start变为true时,div将挂载。所以这就是触发的地方。

尝试将start作为依赖变量传递给useEffect

useEffect(() => {
console.log('elementDimensions', elementDimensions.current?.clientHeight)
} , [start, elementDimensions])

PS。如果不起作用,也将target作为依赖变量传递。

useEffect(() => {
console.log('elementDimensions', elementDimensions.current?.clientHeight)
} , [start, target, elementDimensions])

我认为将代码分为子组件会让这件事变得更简单,因为MouseAim的责任太大了。一般来说,给组件一个明确定义的目的是简化代码库的好方法。

const AimContainer = () => {
const containerRef = useRef()
const [containerSize, setContainerSize] = useState();
useEffect(() => {
// set container size state here
}, [])
return <div ref={containerRef}>...</div>
}
const MouseAim = () => {
const [hasStarted, setHasStarted] = useState(false);
return <div>
{ hasStarted ? 
<AimContainer />
:
<button onClick={() => setHasStarted(true)}>Start</button>
}
</div>
}

AimContainer将在运行useEffect之前进行渲染,因此您可能还希望在设置containerSize之前避免渲染目标。

最新更新