Callback Ref を useCallback の外で参照する
こんにちは、Gaji-Labo アシスタントエンジニアの石垣です。
今回は Callback Ref を useCallback の外で参照する方法についてまとめたいと思います。
やりたいこと
DOM要素を useEffect 等で操作したい時、 useEffect 内で useRef を使った参照をすると、参照したい要素が初期にレンダリングされていない際に参照できないという弱点があります。
const [scrollHeight, setScrollHeight] = useState(0);
const ref = useRef<HTMLDivElement>();
useEffect(() => {
if (ref.current) {
setScrollHeight(ref.current.scrollHeight);
}
}, [ref.current]);
例えば上記の画像のように、最初はレンダリングされていないモーダルの縦幅を取得したい時などは useRef では対応することが出来ません。
その場合は Callback Ref を使用することで要素がレンダリングされているかどうかに関わらず要素の状態を取得・操作することができます。
しかし、Callback Ref を使用すると ref の要素は useCallback の関数内で node として割り当てられるので、外の関数で使用することが出来ません。
これを解決するには、 useCallback の中で ref.current に node を渡します。
解決方法
まずは useRef を作成し、その後 useCallback の中で ref.current に node を渡すことで Callback Ref を useEffect 内で参照することが出来るようになります。
const ref = useRef<HTMLDivElement>();
const setRef = useCallback((node: HTMLDivElement) => {
ref.current = node;
}, []);
コンポーネントには setRef
を渡します。
他の関数の内では通常の ref オブジェクトと同じように ref.current
でDOMノードを取得することが出来ます。
function Sandbox() {
const [items, setItems] = useState<string[]>([]);
const [open, setOpen] = useState(false);
const [scrollWidth, setScrollHeight] = useState(0);
const ref = useRef<HTMLDivElement>();
const setRef = useCallback((node: HTMLDivElement) => {
if (node) {
setScrollWidth(node.scrollHeight);
}
ref.current = node;
}, []);
const handleClickButton = () => {
if (ref.current) {
setItems([...items, "item"]);
setScrollWidth(ref.current.scrollHeight);
}
};
return (
<div>
<Button
variant="contained"
onClick={() => {
setOpen(true);
}}
>
Open Modal
</Button>
<Modal
open={open}
onClose={() => {
setOpen(false);
}}
>
<Box sx={style}>
<div ref={setRef}>
<Button variant="contained" onClick={handleClickButton}>
Add Item
</Button>
<Typography variant="h6" component="h2">
scrollWidth: {scrollWidth}
</Typography>
{items.map((item, index) => (
<Typography variant="h6" component="h2">
{item}
{index}
</Typography>
))}
</div>
</Box>
</Modal>
</div>
);
}
まとめ
今回は Callback Ref を useEffect 内で参照する方法についてまとめました。
最初はレンダリングされていない要素を操作するということで、使い所は少なくないかなと思います。
React コンポーネントを実装する際に参考にしていただければ幸いです。
Gaji-Laboでは、React経験が豊富なフロントエンドエンジニアを募集しています
弊社ではReactの知見で事業作りに貢献したいフロントエンドエンジニアを募集しています。大きな制作会社や事業会社とはひと味もふた味も違うGaji-Laboを味わいに来ませんか?
もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください。お仕事お問い合わせや採用への応募、共に大歓迎です!
求人応募してみる!