0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Reactについて学んだことをまとめる(その2)

Posted at

以下のUdemyの講座を受講したので、学んだことをまとめていく。

レンダリングの最適化

再レンダリングされる条件と再レンダリングによるパフォーマンス低下が起こる例、及びそれを解消するための方法について学んだ。

再レンダリングされる条件

  1. stateが更新されたコンポーネントは再レンダリングされる
  2. propsが変更されたコンポーネントは再レンダリングされる
  3. 再レンダリングされたコンポーネント配下の子要素は再レンダリングされる
//App.jsx
import './App.css';
import { ChildArea } from './ChildArea';

export const App = () => {
  const [text, setText] = useState('');
  const [open, setOpen] = useState(false);

  const onChangeText = (e) => setText(e.target.value);

  const onClickCountUp = () => {
    setConut(count + 1);
  };

  const onClickOpen = () => {
    setOpen(!open);
  };

  return (
    <>
      <input value={text} onChange={onChangeText} />
      <br />
      <br />
      <button onClick={onClickOpen}>表示</button>
      <ChildArea open={open} />
    </>
  );
};
//ChildArea.jsx
export const ChildArea = (props) => {
  const { open } = props;
  console.log('ChildAreaがレンダリングされた!');

  const data = [...Array(2000).keys()];
  data.forEach(() => {
    console.log('...');
  });

  return (
    <>
      {open ? (
        <div style={style}>
          <p>子こんぽーねんと</p>
        </div>
      ) : null}
    </>
  );
};

上記の例では、「表示」ボタンを押すとChildAreaに渡されているopenというPropsが更新されるので、ChildAreaコンポーネントは再レンダリングされる。
テキストボックスが更新された場合、textステートが更新されるので、Appコンポーネントが再レンダリングされる。
その際、ChildAreaコンポーネントはAppコンポーネントの子要素にあたるため、ChildAreaコンポーネントも再レンダリングされてしまう。

memo関数

memoという関数を使うと、Propsが変更された時のみ再レンダリングするようにできる。
コンポーネントをmemo関数で囲むように使用する。

//ChildArea.jsx
export const ChildArea = memo((props) => {
  const { open } = props;
  console.log('ChildAreaがレンダリングされた!');

  const data = [...Array(2000).keys()];
  data.forEach(() => {
    console.log('...');
  });

  return (
    <>
      {open ? (
        <div style={style}>
          <p>子こんぽーねんと</p>
        </div>
      ) : null}
    </>
  );
});

この場合、表示ボタンを押すとPropsが更新されるので再レンダリングされる。
テキストボックスが更新された場合は親コンポーネントの再レンダリングが起きるが、memo関数により、ChildAreaコンポーネントは再レンダリングされない。

useCallback関数

下記の例では、PropsにonClickCloseという関数を渡しているが、Propsに毎回新しい関数を生成して渡している扱いになるため、ChildAreaコンポーネントの再レンダリングが発生する。

//App.jsx
import { useState } from 'react';
import './App.css';
import { ChildArea } from './ChildArea';

export const App = () => {
  const [text, setText] = useState('');
  const [open, setOpen] = useState(false);

  const onChangeText = (e) => setText(e.target.value);

  const onClickClose = () => setOpen(false);

  const onClickOpen = () => {
    setOpen(!open);
  };

  return (
    <>
      <input value={text} onChange={onChangeText} />
      <br />
      <br />
      <button onClick={onClickOpen}>表示</button>
      <ChildArea open={open} onClickClose={onClickClose} />
    </>
  );
};
//ChildArea.jsx
import { memo } from 'react';

const style = {
  width: '100%',
  height: '200px',
  backgroundColor: 'khaki',
};

export const ChildArea = memo((props) => {
  const { open, onClickClose } = props;
  console.log('ChildAreaがレンダリングされた!');

  const data = [...Array(2000).keys()];
  data.forEach(() => {
    console.log('...');
  });

  return (
    <>
      {open ? (
        <div style={style}>
          <p>子こんぽーねんと</p>
          <button onClick={onClickClose}>閉じる</button>
        </div>
      ) : null}
    </>
  );
});

useCallback関数を使用すると、条件を満たした場合のみ再生成するようにできる。
これにより、関数の再生成による再レンダリングを抑制できる。

const onClickClose = useCallback(() => setOpen(false), []);

使い方はuseEffectと同様、第1引数に関数の処理を記述する。
useEffectの第2引数に空配列を指定すると、マウント時(ページが最初に読み込まれる時)に1度だけ生成される。
第2引数の配列要素に変数を指定すると、指定した変数が更新された場合に第1引数に記述した関数が再生成される。

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?