みかづきブログ・カスタム

基本的にはちょちょいのほいです。

useRefの使い所 📝

f:id:kimizuka:20210416093201p:plain

ja.reactjs.org

useRefといえば、DOMにアクセスする手段としておなじみ ですが、なんとなく使い所がわかったようなわからないような気がしたのでメモを残しておきます。

ja.reactjs.org

1秒間に1回、useStateを使ってtextを更新するコードを書いた際、下記の書き方だとコンソールには'hello'が表示され続けます。

const [text, setText] = React.useState('hello');
const textRef = React.useRef(text);

React.useEffect(() => {
  setInterval(() => {
    console.log(text); // => hello
    setText(String(Date.now()));
  }, 1000);
}, []);

なぜならば、useStateは、再レンダー間で同一性が保たれ、変化しないことを保証されているからです。

ja.reactjs.org

consoleに更新されたtextを表示したい場合は、下記のような書き方になります。

const [text, setText] = React.useState('hello');
const textRef = React.useRef(text);

React.useEffect(() => {
  setInterval(() => {
    console.log(text); // => hello
    setText(String(Date.now()));
  }, 1000);
}, []);

React.useEffect(() => {
  console.log(text);
}, [text]);

ほとんどの場合はこれでOKなのですが、のっぴきならない理由で、

const [text, setText] = React.useState('hello');

React.useEffect(() => {
  setInterval(() => {
    console.log(text); // ← ここの部分
    setText(String(Date.now()));
  }, 1000);
}, []);

ここの部分で最新のtextを表示しなければならないとしましょう。(そんなケースはないと思いますが)
そんなときは、useRefを使えば一応実現できます。

const [text, setText] = React.useState('hello');
const textRef = React.useRef(null);

React.useEffect(() => {
  setInterval(() => {
    console.log(textRef.current);
    setText(String(Date.now()));
  }, 1000);

React.useEffect(() => {
  textRef.current = text;
}, [text]);
}, []);

なぜならば、useRefで返されるオブジェクトは、コンポーネントの存在期間全体にわたって存在し続けるからです。また、useRefの値を更新してもコンポーネントに再レンダリングが走らないこともポイントです。

ja.reactjs.org


DEMO

こちらの例を見るとuseStateの値は不変、useRefの値は変動していることがわかります。
肝心の使い所ですが、

・コンポーネントの存在期間全体にわたって存在し続けることができる
・コンポーネントの再レンダリングが走らない

の特性を活かせるポイントがなかなか見つからずでしたが、直近だと、localStorageに書き込む値(画面には表示しない)の管理に使ってみました。
なぜならば、useStateで管理すると、画面には表示しないにもかかわらず再レンダリングが走ってしまうからです。
かなり特殊な例だと思いますし、きっとベストプラクティスではないんだと思います。

今後も使い所を探してみます。