1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

5分で読むReact豆知識 | 入門 | 第9回: レンダリングの落とし穴

Posted at

はじめに

このシリーズは、Reactの公式ページなどから特に重要と感じたトピックを選び出し、何度でも読み返せるよう簡潔にまとめたものです。:orange_book:

進捗管理には「いいね」か「ブックマーク」がお勧め
さらに僕のモチベーションアップになります!:thumbsup:よろしくお願いいたします!:thumbsup:

シリーズ一覧はこちら

stateで定義したオブジェクトを直接更新してはいけない理由

このパターンはレンダリングされる?-①

下記のコードで表示されたボタンを押した時に、画面は最新の値でレンダリングされるでしょうか?

export default function ObjectState() {
  const [position, setPosition] = useState({
    x: 0,
    y: 0
  });
  return (
    <>
        <button
          onClick={() => {
            position.x = position.x+1;
            position.y = position.y+1;
          }}>up
        </button>
        <div>
          {position.x}
        </div>
    </>
  );
}

結果はレンダリングされません。
レンダリングされない理由は、setPosition関数を利用していないためです。オブジェクト自体の参照値を見ているため、オブジェクトの中身を直接変更してもReactはその変更を検知しません。このため、上記の更新方法はやめましょう。

更新例〜スプレット構文を使う〜

setPosition({
  ...position,
  y: e.clientY
});

更新例〜ローカルミューテーションを利用した更新〜

const nextPosition = {};
nextPosition.x = e.clientX;
nextPosition.y = e.clientY;
setPosition(nextPosition);

##このパターンはレンダリングされる?-②
下記のパターンは再レンダリングされるでしょうか?

import { useRef ,useState } from 'react';

export default function Counter() {
  let countRef = useRef(0);
  const [count, setCount] = useState(0);

  function handleClick() {
    // This doesn't re-render the component!
    countRef.current = countRef.current + 1;
    setCount(0);
  }

  return (
    <button onClick={handleClick}>
      You clicked {countRef.current} times
    </button>
  );
}

答えは再レンダリングされません。
handleClickでsetCountを使っているから再レンダリングされるのでは?となりますがReactは前回の値と比較し変化しなければレンダリングのトリガーにはせずレンダリングをスキップします。

ローカル変数はレンダー間で保持されない

hooksでuseStateやuseRefを使う理由の一つは、レンダー間で値を保持することです。

ローカル変数は、レンダリング間で保持されないため初期値に戻され、
useStateのようなレンダリングのトリガーがないため変更に気づけません。

下記のような処理を書いてもレンダリングはされないため注意しましょう。

let count = 0; // 再レンダリングされると初期化されます
function handleClick() {
    count = count + 1;
}
<button onClick={handleClick}>
    next
<button/>
{/* 値の変更がトリガにならないため描画される値は変わりません */}
<div>{count + 1}<div/>

(参考ページ参照)ボタンをクリックしてカウントを上げていっても表示は変わらない。また、修正して再レンダリングが始まるとカウントは0に初期化されます。

参考サイト

より詳しく学びたい方はこちら

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?