Udemy で React Hooks を学んだ時のメモ useState & useEffect編


今年の正月でudemyを使ってReact Hooksについて学んだのでメモをアウトプットしておく。

今回お世話になったコースはこちら

React Hooks 入門 - Hooksと Redux を組み合わせて最新のフロントエンド状態管理手法を習得しよう!

ご存知の方も多いと思うがUdemyは頻繁にセールを行っている為お安く購入する事が可能。
セール時に購入すれば手を動かすチュートリアル的な存在としては激安だと思うので興味のある方にはお勧めしたい。

UseState

useStateは値の変更によって自身の再描画をスケジューリングさせる機能。

import React, { useState } from "react";
const App = () => {
  const output = useState(1000);
  console.log({ output });

  return <div>This is a templete for react-app</div>;
};
export default App;

useSateは常に配列を返す事が分かる

import React, { useState } from "react";
const App = () => {
  const [count, setCount] = useState(0);

  return <div>This is a templete for react-app</div>;
};

export default App;

簡単なカウントアプリの実装

import React, { useState } from "react";
const App = () => {
  const [count, setCount] = useState(0);
  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);
  return (
    <>
      <div>count: {count}</div>
      <button onClick={increment}>+1</button>
      <button onClick={decrement}>-1</button>
    </>
  );
};
export default App;

setCountの引数に関数を渡すパターン

import React, { useState } from "react";
const App = () => {
  const [count, setCount] = useState(0);

  const increment2 = () => setCount(previousCount => previousCount + 1);
  const decrement2 = () => setCount(previousCount => previousCount - 1);

  return (
    <>
      <div>count: {count}</div>
      <div>
        <button onClick={increment}>+1</button>
        <button onClick={decrement}>-1</button>
      </div>
    </>
  );
};
export default App;

previousCountは現在の状態を受け取る事ができる
なのでcでもpでも何でも良い

×2や倍数の時だけ3で割るも実装したもの

import React, { useState } from "react";
const App = () => {
  const [count, setCount] = useState(0);
  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);
  const increment2 = () => setCount(previousCount => previousCount + 1);
  const decrement2 = () => setCount(previousCount => previousCount - 1);

  const reset = () => setCount(0);
  const double = () => setCount(count * 2);
  const devide3 = () =>
    setCount(previousCount =>
      previousCount % 3 === 0 ? previousCount / 3 : previousCount
    );

  return (
    <>
      <div>count: {count}</div>
      <div>
        <button onClick={increment}>+1</button>
        <button onClick={decrement}>-1</button>
      </div>
      <div>
        <button onClick={increment2}>+1</button>
        <button onClick={decrement2}>-1</button>
      </div>
      <div>
        <button onClick={reset}>Reset</button>
        <button onClick={double}>×2</button>
        <button onClick={devide3}>3の倍数の時だけ3で割る</button>
      </div>
    </>
  );
};
export default App;

複数の状態を管理しよう

import React, { useState } from "react";
const App = () => {
  const initialStates = {
    name: "",
    price: 1000
  };
  const [name, setName] = useState(initialStates.name);
  const [price, setPrice] = useState(initialStates.price);
  return (
    <>
      <p>
        現在の{name}は、{price}円です。
      </p>
    </>
  );
};
export default App;

propsを想定した書き方

import React, { useState } from "react";
const App = props => {
  const [name, setName] = useState(props.name);
  const [price, setPrice] = useState(props.price);
  return (
    <>
      <p>
        現在の{name}は、{price}円です。
      </p>
    </>
  );
};
App.defaultProps = {
  name: "",
  price: 1000
};
export default App;

実装

import React, { useState } from "react";
const App = props => {
  const [name, setName] = useState(props.name);
  const [price, setPrice] = useState(props.price);
  return (
    <>
      <p>
        現在の{name}は、{price}円です。
      </p>
      <button onClick={() => setPrice(price + 1)}>+1</button>
      <button onClick={() => setPrice(price - 1)}>-1</button>
      <button onClick={() => setPrice(props.price)}>Reset</button>
      <input value={name} onChange={e => setName(e.target.value)} />
    </>
  );
};
App.defaultProps = {
  name: "",
  price: 1000
};
export default App;

リセットの実装

import React, { useState } from "react";
const App = props => {
  const [name, setName] = useState(props.name);
  const [price, setPrice] = useState(props.price);
  const reset = () => {
    setPrice(props.price);
    setName(props.name);
  };
  return (
    <>
      <p>
        現在の{name}は、{price}円です。
      </p>
      <button onClick={() => setPrice(price + 1)}>+1</button>
      <button onClick={() => setPrice(price - 1)}>-1</button>
      <button onClick={reset}>Reset</button>
      <input value={name} onChange={e => setName(e.target.value)} />
    </>
  );
};
App.defaultProps = {
  name: "",
  price: 1000
};
export default App;

複数の状態を1つのオブジェクトに統合しよう

stateというオブジェクトで全ての状態を管理してリファクタリングしたもの

import React, { useState } from "react";
const App = props => {
  const [state, setState] = useState(props);
  const { name, price } = state;
  return (
    <>
      <p>
        現在の{name}は、{price}円です。
      </p>
      <button onClick={() => setState({ ...state, price: price + 1 })}>
        +1
      </button>
      <button onClick={() => setState({ ...state, price: price - 1 })}>
        -1
      </button>
      <button onClick={() => setState(props)}>Reset</button>
      <input
        value={name}
        onChange={e => setState({ ...state, name: e.target.value })}
      />
    </>
  );
};
App.defaultProps = {
  name: "",
  price: 1000
};
export default App;

UseEfefct

UseEfefctとは

ライフサイクルメソッドの代替えとなるものでcomponentDidMountによく似ている。

UseEffectはいつ呼ばれるのか

前回のコードにuseEffectを仕込んだ。

import React, { useEffect, useState } from "react";
const App = props => {
  const [state, setState] = useState(props);
  const { name, price } = state;
  useEffect(() => {
    console.log("useEfect is invoked");
  });
  const renderPreriod = () => {
    console.log("renderPreriod renders preiod.");
    return "。";
  };
  return (
    <>
      <p>
        現在の{name}は、{price}円です{renderPreriod()}
      </p>
      <button onClick={() => setState({ ...state, price: price + 1 })}>
        +1
      </button>
      <button onClick={() => setState({ ...state, price: price - 1 })}>
        -1
      </button>
      <button onClick={() => setState(props)}>Reset</button>
      <input
        value={name}
        onChange={e => setState({ ...state, name: e.target.value })}
      />
    </>
  );
};
App.defaultProps = {
  name: "",
  price: 1000
};
export default App;

これを実行すると以下のようなログが出力される。

DOMが呼ばれる時、DOMが最初に描画される時、DOMの中の要素がどこか変更された時にuseEffectが実行された。
つまり従来のreactのcomponentDidMountやcomponentDidUpdateに似ているという事がこのログから分かる。

componentDidmountのように最初のレンダリングの時のみに実行したい場合は?

これはパターンで決まっていてこのように空の配列を渡す。

useEffect(() => {
  console.log("This is like componentDidMopunt");
}, []);

なので例えば以下のようなuseEffectが2つあるコードを実行した場合は

useEffect(() => {
    console.log("This is like componentDidMopunt or componentDidUpdate.");
});

useEffect(() => {
    console.log("This is like componentDidMopunt");
}, []);

といったように複数書いた場合は

下の配列を渡したuseEffectは一度した実行されない。