Home > Software engineering >  React useRef is not setting even in useEffect
React useRef is not setting even in useEffect

Time:08-17

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.

  • Related