Search code examples
javascriptreactjsuse-effectuse-stateuse-ref

getElementById after dynamically adding it in React


I am adding Cards dynamically in my React functional component. Cards are stored in State. I map them and give id to each of them. OnClick on those Cards I get their id successfully. Now I want to getElementById to change Card color:

function Clicked(pressedGifId) {
  if (pressedGifId === 'correctGif') CorrectMatch();
  else WrongMatch();
}

function CorrectMatch(pressedGifId) {
  // / THERE I GET Element: null
  console.log('Element:', document.getElementById(pressedGifId));
}
function WrongMatch() {
  console.log('wrong a match!');
}

export default function GameObject(props) {
  const addedToGameGif = [];
  const [pressedGifId, gifPressed] = useState(null);
  const [photoCards, setPhotoCards] = useState([]);

  useEffect(() => {
    Clicked(pressedGifId);
  }, [pressedGifId]);

  // add randomly picked photos to addedToGameGif array
  // ...

  addedToGameGif.map(gifId =>
    photoCards.push(
      <Card id={gifId} onClick={() => gifPressed(gifId)}>
        text
      </Card>,
    ),
  );

  return <div>{photoCards}</div>;
}

I tried learning refs but they are only for class components. So how do I reach my element by id in React?


Solution

  • You can use ref in functional component as well. There is a hook called useRef.

    Note: Never interact directly with DOM until or unless there is no api available in react to solve the problem for that particular use case.

    In react it's not recommended to interact directly with dom. Always use react apis to interact with dom. React is designed to hide the DOM because they want to abstract the DOM away. By using the DOM directly you break the abstraction and make your code brittle to changes introduced in the library.

    React is maintaining a virtual DOM if we make any changes in actual DOM directly then react will not be aware of this change and this can lead to some unexpected behavior .

    import React, {useState, useRef} from 'react';
    
    export default function GameObject(props) {
      const addedToGameGif = [];
      const [pressedGifId, gifPressed] = useState(null);
      const [photoCards, setPhotoCards] = useState([]);
      const elemRef = useRef(null);
    
      useEffect(() => {
        Clicked(pressedGifId);
      }, [pressedGifId]);
    
      // add randomly picked photos to addedToGameGif array
      // ...
    
      addedToGameGif.map(gifId =>
        photoCards.push(
          <Card ref={elemRef} id={gifId} onClick={() => gifPressed(gifId)}>
            text
          </Card>
        )
      );
    
      return <div>{photoCards}</div>;
    }
    
    

    Example from official docs.

    function TextInputWithFocusButton() {
      const inputEl = useRef(null);
      const onButtonClick = () => {
        // `current` points to the mounted text input element
        inputEl.current.focus();
      };
      return (
        <>
          <input ref={inputEl} type="text" />
          <button onClick={onButtonClick}>Focus the input</button>
        </>
      );
    }