Search code examples
reactjsreferencebuttonclickuse-effect

How can you make a createRef/useRef not target the last value. But rather go to where its suppose to


Can't manage to make useRef/createRef to get any other div's other then what was added last. How can i make it so when the button is clicked the ref to the div changes.

I've tried with both useRef and createRef. Since I want to make a new instance of ref, i've looked more into createRef rather then useRef. I've also played around useEffect. But my solution didn't help me with my biggest problem

I have made a small project containing 3 components to help you understand what I'm trying to explain.

I also have a database containing mock data -> in my real project this isn't the problem. It's an array containing objects. [{'id':'1', 'name':'first'},...]

Main:

const MainComponent = () => {
    const dataRef = React.createRef(null) 

    React.useEffect (() => {
        if(dataRef && dataRef.current){
            dataRef.current.scrollIntoView({ behavior:'smooth', block:'start' })
       }
    },[dataRef])

    const _onClick = (e) => {
        dataRef.current.focus(); 
    }

    return(
        <>
        {data && data.map((entry, index) =>{
            return <ButtonList 
                key={index}
                entry={entry}
                onClick={_onClick}
            /> 
        })}

        {data && data.map((entry, index) =>{
            return <ListingAllData  
                key={index}
                dataRef={dataRef}
                entry={entry}
                index={index}/>
        })}
        </>
    )
}

Button Component

const ButtonList = ({ entry, onClick }) => {
    return <button onClick={onClick}>{entry.name}</button>
}

Listing data component

const ListingAllData = (props) => {
    const {entry, dataRef } = props; 
    return (
        <div ref={dataRef}>
            <p>{entry.id}</p>
            <p>{entry.name}</p>
        </div>
    );
}

I've console logged the data.current, it only fetches the last element. I hoped it would fetch the one for the button I clicked on.


Solution

  • I think the main idea here is to create dynamic refs for each element (array of refs), that's why only the last one is selected when app renders out.

    const MainComponent = () => {
      const dataRefs = [];
    
      data.forEach(_ => {
        dataRefs.push(React.createRef(null));
      });
    
      const _onClick = (e, index) => {
        dataRefs[index].current.focus();
        dataRefs[index].current.scrollIntoView({
          behavior: "smooth",
          block: "start"
        });
      };
    
      return (
        <>
          {data &&
            data.map((entry, index) => {
              return (
                <ButtonList
                  key={index}
                  entry={entry}
                  onClick={e => _onClick(e, index)}
                />
              );
            })}
    
          {data &&
            data.map((entry, index) => {
              return (
                <>
                  <ListingAllData
                    key={index}
                    dataRef={dataRefs[index]}
                    entry={entry}
                    index={index}
                  />
                </>
              );
            })}
        </>
      );
    };
    

    Created working example in code sandbox.

    https://codesandbox.io/s/dynamic-refs-so25v