React.js 学習備忘録②~再レンダリング~

# 再レンダリングの条件
・stateが更新された時
・propsが変更された時
・親のコンポーネントが更新された時

# inputについて
inputはvalue属性が設定されてると入力ができなくなる。流れとしては
1.inputエリアに入力
2.stateであるtextの変更を検知
3.textを更新するため、再レンダリングされる
4.useState("")が走り、初期値として空白が設定される。
そのため下記のように、onChangeイベントを設定する必要がある。
```React.js App.jsx
import { useState } from "react";

//動かない
export const App = () => {
    const [text, setText] = useState("");
    return(
        <>
            <input value="text" />
        </>
    );
};

//動く
export const App = () => {
    const [text, setText] = useState("");
    const onChangeText = (e) => {
        setText(e.target.value)
    }
    return(
        <>
            <input value="text" onChange={onChangeText} />
        </>
    );
};
```

# useEffect
・特定のstateが更新された時のみ処理を行う
・第１引数に行いたい処理、第２引数に配列形式で対象となるstateを指定する
・空の配列を渡すことで、最初のレンダリング時のみ処理を行うことができる
```React.js App.jsx
import './App.css';
import { useEffect, useState } from 'react';

export const App　= () => {
  const [hoge, setHoge] = useState(true);
  const [fuga, setFuga] = useState(true);
  const onClickHoge = () => {
    setHoge(!hoge);
  }
  const onClickFuga = () => {
    setFuga(!fuga);
  }
  useEffect(() => {
    console.log('最初のレンダリングの時のみ')
  },[])
  useEffect(() => {
    console.log('hogeが変更された時のみ')
  },[hoge])
  console.log('stateが変わるたび');
  
  return (
    <>
      <button onClick={onClickHoge}>hoge</button>
      <div>{hoge || 'hoge'}</div>
      <button onClick={onClickFuga}>fuga</button>
      <div>{fuga || 'fuga'}</div>
    </>
  );
};

```

# メモ化
・コンポーネントや値を再利用できるように保持すること
・再レンダリングを防ぐことができる

# memo
・コンポーネントをメモ化する
・下記であればChildコンポーネントは親コンポーネントがレンダリングされる度に再レンダリングされるが、MemoChildは再レンダリングされない
``` App.jsx
import { useState } from 'react';
import { Child } from './componentes/Child';
import { MemoChild } from './componentes/MemoChild';

export const App　= () => {
  const [num, setNum] = useState(0);
  const onClickIncrement = () => {
    setNum(num + 1);
  };
  return (
    <>
      <button onClick={onClickIncrement}>stateを更新</button> //クリックされる度にstateが更新されるため、再レンダリング
      {num}
      //　親が再レンダリングされると、通常子コンポーネントは再レンダリングされるが、MemoChildの方は再レンダリングされない
      <MemoChild /> //メモ化されたコンポーネント
      <Child /> //通常のコンポーネント
    </>
  );
};
```
``` Child.jsx
export const Child = () => {
    console.log('通常のコンポーネント'); //上記のonClickIncrementが走る度に出力される
};
```

``` MemoChild.jsx
import { memo } from "react"

export const MemoChild = memo(() => {
    console.log('メモ化したコンポーネント'); //上記のonClickIncrementが走っても出力されない
});
```

# useCallback
・関数をメモ化する
・第１引数にメモ化する関数を、第２引数に再計算の対象となるstateを指定する
・関数をpropsとして渡してるコンポーネント自身が再レンダリングされた際に、渡されたコンポーネントが再レンダリングされるのを防ぐ
``` App.jsx
import { useCallback, useState } from 'react';
import { Child } from './componentes/Child';
import { HogeChild } from './componentes/HogeChild';
import { MemoChild } from './componentes/MemoChild';

export const App　= () => {
  const [hoge, setHoge] = useState(true);
  const onClickHoge = () => {
    setHoge(!hoge);
  };
  const [fuga, setFuga] = useState(true);
  const onClickFuga = () => {
    setFuga(!fuga);
  };
  const notMemoFunction = () => console.log('メモ化されていない関数'); //アロー関数はレンダリングの度に新たな関数として再計算されるため、propsの変更と見なされる
  const memoFunction = useCallback(() => console.log('メモ化されている関数'),[]); //メモ化されてるためレンダリングされても再計算されない
  const hogeFunction = useCallback(() => console.log('hogeに対してのみメモ化されていない関数'),[hoge]); //hogeが更新された時のみ再計算される
  return (
    <>
      <button onClick={onClickHoge}>hoge</button>
      {hoge || 'hoge'}
      <button onClick={onClickFuga}>fuga</button>
      {fuga || 'fuga'}
      <MemoChild memoFunction={memoFunction}/>
      <Child notMemoFunction={notMemoFunction}/>
      <HogeChild hogeFunction={hogeFunction} />
    </>
  );
};

```
``` Child.jsx
import { memo } from "react";

export const Child = memo((props) => {
    props.notMemoFunction() //コンポーネントがメモ化されていても、再レンダリング時関数が再計算される=propsの変更があったと見なされ再レンダリングされる
});
```
``` MemoChild.jsx
import { memo } from "react"

export const MemoChild = memo((props) => {
    props.memoFunction() //propsとして渡される関数がメモ化されているため、再レンダリングされない
});
```
``` HogeChild.jsx
import { memo } from "react"

export const HogeChild = memo((props) => {
    props.hogeFunction()//propsとして渡される関数がhogeを対象として再計算されているため、上記onClickHogeでのみ再レンダリングされる
});
```

# useMemo
・計算結果をメモ化する
・第１引数にメモ化する関数を、第２引数に再計算の対象となるstateを指定する
・useCallbackと違い、コンポーネント自身の関数の再計算を防ぐ
``` App.jsx
import { useState, useMemo } from 'react';

export const App = () => {
  const [hoge, setHoge] = useState(true);
  const onClickHoge = () => {
    setHoge(!hoge);
  };
  const [fuga, setFuga] = useState(true);
  const onClickFuga = () => {
    setFuga(!fuga);
  };

  console.log('メモ化されていない')
  useMemo(() => console.log('メモ化されている'),[]);
  useMemo(() => console.log('hogeに対してはメモ化されていない'),[hoge]);
  return (
    <>
      <button onClick={onClickHoge}>hoge</button>
      {hoge || 'hoge'}
      <button onClick={onClickFuga}>fuga</button>
      {fuga || 'fuga'}
    </>
  );
};
```

何かミスやアドバイス等ありましたら、コメント頂けると幸いです。

前回:[基本事項について](https://qiita.com/_takenoko/items/7b8bbdc503f658d74849)
次回：[CSSについて](https://qiita.com/_takenoko/items/e1e5b49d328e95c62338)