React의 useRef를 사용하기위한주의 사항

11117 단어 React후크

소개



DOM의 바운딩 박스 등을 취득하는 경우, hooks 의 useRef 를 사용하고 싶은 경우가 있습니다. 그러나, 하위의 컴퍼넌트에 그러한 정보를 건네주는 경우 등은, 주의가 필요하게 됩니다.

예



다음과 같이 메시지의 오른쪽 하단에 좋아요 버튼을 표시하고 싶습니다.


작동하지 않는 패턴


ThumbsUp 컴퍼넌트에 대해 버튼 위치의 기준이 되는 HTMLElement 를 건네주어, ThumbsUp 로 버튼의 표시 위치를 요구하고 있습니다.

App.tsx
import React, { useRef } from "react";
import ThumbsUp from "./ThumbsUp";
import "./styles.scss";

export default function App() {
  const ref = useRef<HTMLSpanElement | null>();
  return (
    <div>
      <span ref={ref} className="baloon">
        こんにちは
      </span>
      {ref.current && (
        <ThumbsUp anchor={ref.current} />
      )}
    </div>
  );
}


ThumbsUp.tsx
import React, { useRef, useEffect } from "react";
import "./styles.scss";

type Props = {
  anchor: HTMLElement;
};
export default function Portal({ anchor }: Props) {
  const ref = useRef<HTMLDivElement | null>(null);
  useEffect(() => {
    if (ref.current) {
      const anchorRect = anchor.getBoundingClientRect();
      const position = {
        top: anchorRect.top + anchorRect.height - 20,
        left: anchorRect.left + anchorRect.width - 10
      };
      ref.current.style.top = `${position.top}px`;
      ref.current.style.left = `${position.left}px`;
    }
  }, [anchor]);
  return (
    <div ref={ref} className="thumbs-up">
      <button>
        <span role="img" aria-label="thumbs-up">
          👍
        </span>
      </button>
    </div>
  );
}

무슨 일이 일어나는가



불행히도 의도대로 좋아요 버튼이 표시되지 않습니다.

이것은 공식 에도 기재되어 있습니다만, App.tsx 에 있어서, ref.current 가 바뀌어도 재렌더는 발생하지 않기 때문입니다. 따라서 ThumbsUp 는 렌더링되지 않은 상태입니다.

해결책



콜백 식의 ref 와 useState 를 사용하는 것으로 해결할 수 있습니다. 아래와 같이 span 의 ref 에 함수를 주는 것으로, setAnchor 가 불려진 타이밍에 재렌더를 발생시켜, 의도대로 ThumbsUp 를 렌더링 할 수 있게 됩니다.

App.tsx
import React, { useState } from "react";
import ThumbsUp from "./ThumbsUp";
import "./styles.scss";

export default function App() {
  const [anchor, setAnchor] = useState<HTMLSpanElement>();
  return (
    <div>
      <span
        ref={elm => { // コールバック関数を与える
          setAnchor(elm);
        }}
        className="baloon"
      >
        こんにちは
      </span>
      {anchor && <ThumbsUp anchor={anchor} /> }
    </div>
  );
}

좋은 웹페이지 즐겨찾기