I am attempting to show an image in a random position according to dynamic dimensions using a ref. However, when getting the useRef's current value, I recieve undefined likely because the useRef's target div hasn't loaded yet.
To overcome this, I am using a useEffect to wait for the target div to load.
However, when I run the program the image does not move because the useRef's current value remains at 0. The
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
CodePudding user response:
Just set your target initial state to 'false', then set it to true when 'ref' is ready
Try this one:
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])
CodePudding user response:
Just like you mentioned, the useRef is set very initially when the div has not been mounted.
The div mounts when start
becomes true. So that's where the trigger needs to happen.
Try to pass start
as the dependency variables to the useEffect
.
useEffect(() => {
console.log('elementDimensions', elementDimensions.current?.clientHeight)
} , [start, elementDimensions])
PS. If it doesn't work, pass target
as the dependency variable as well.
useEffect(() => {
console.log('elementDimensions', elementDimensions.current?.clientHeight)
} , [start, target, elementDimensions])
CodePudding user response:
I think separating your code into subcomponents will make this much simpler, as MouseAim
has too much responsibility. In general, giving your components one clearly-defined purpose is a good way to simplify your codebase.
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
will render before running useEffect
, so you may also want to avoid rendering the target until containerSize
is set.