React Hooksの使い方を学ぼう~関数コンポーネントの状態管理を行う

基本のフック

useState

useState とは、関数コンポーネントでstateを管理( state の保持と更新)するためのReactフックであり、最も利用されるフックです。 state とはコンポーネントが内部で保持する「状態」のことで、画面上に表示されるデータ等、アプリケーションが保持している状態を指しています。

const App = () => {
  const [count, setCount] = React.useState(0);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>クリック {count} 回</button>
    </div>
  );
}
ReactDOM.render(<App />, document.getElementById("root"));

useEffect

useEffect とは、関数の実行タイミングをReactのレンダリング後まで遅らせるhookです。 副作用の処理(DOMの書き換え、変数代入、API通信などUI構築以外の処理)を関数コンポーネントで扱えます。 クラスコンポーネントでのライフサイクルメソッドに当たります。

const App = () => {
    const [count, setCount] = React.useState(0)
    const [text, setText] = React.useState('')
    const [isShow, setShow] = React.useState(true)

    // レンダリングされた時にのみ実行
    React.useEffect(() => {
        console.log(`レンダリングされました。`)
    }, [])

    // count または textが更新された時に実行
    React.useEffect(() => {
        console.log(`count または textが更新されました。count=${count} text=${text}`)
    }, [text, count])

    // コンポーネントが削除される際に実行
    React.useEffect(() => {
        return () => {
            console.log('コンポーネントが削除されました。')
        };
    }, [])

    return (
        <div>
            <input type='text' value={text} onChange={e => setText(e.target.value)}></input>
            <button onClick={() => setCount(count + 1)}>クリック {count} 回</button>
        </div>
    )
}

ReactDOM.render(<App />, document.getElementById("root"));

useContext

useContext とは、 Context 機能をよりシンプルに使えるようになった機能。 親からPropsで渡されていないのに、 Context に収容されているデータへよりシンプルにアクセスできるというものです。


// Contextを生成する
const NumberContext = React.createContext();
// 2つの値を持つオブジェクトを返す
// { Provider, Consumer }

const App = () => {
  // Providerを活用して値を全ての
  // 子要素と孫要素に利用可能にする
  return (
    <NumberContext.Provider value={42}>
      <div>
        <Display />
      </div>
    </NumberContext.Provider>
  );
}

const Display = () => {
  // Consumerを活用してcontextから取得した値を使う
  // このコンポーネントはpropsを取らないことに注意!
  return (
    <NumberContext.Consumer>
      {value => <div>親コンポーネントから渡された値: {value}</div>}
    </NumberContext.Consumer>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

追加のフック

useReducer

useReducer は useState と同様にstateを管理することができるフックです。 useState との使い分けは、stateが複数の値にまたがるような複雑なロジックがある場合・前回のstateに基づいてstateを更新する場合に useReducer を使うのがよいとされています


const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
  }
}

const App = () => {
  const [state, dispatch] = React.useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}
ReactDOM.render(<App />, document.getElementById("root"));

useCallback

useCallbackとは パフォーマンス向上のためのフックで、メモ化したコールバック関数を返します。 useEffectと同じように、依存配列(=[deps] コールバック関数が依存している要素が格納された配列)の要素のいずれかが変化した場合のみ、メモ化した値を再計算します。


const App = () => {
  const [count, setCount] = React.useState(0);

  const handleClick = React.useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <button onClick={handleClick}>クリック</button>
      <p>count: {count}</p>
    </div>
  );
};
ReactDOM.render(<App />, document.getElementById("root"));

useMemo

useMemoとは関数の結果を保持するためのフックで、何回やっても結果が同じ場合の値などを保存(メモ化)し、そこから値を再取得します。 不要な再計算をスキップすることから、パフォーマンスの向上が期待出来ます。


const App = () => {
  const [countNormal, setCountNormal] = React.useState(0);
  const [countHeavy, setCountHeavy] = React.useState(0);

  const heavyFunction = (count) => {
    let i = 0;
    while (i < 1000000000) i++;
    return count;
  };
  
  // 関数の結果をメモ化する
  const heavyFunctionMemo = React.useMemo(() => heavyFunction(countHeavy), [
    countHeavy,
  ]);

  return (
    <div className="app">
      <div className="app-counter">
        <div>通常の処理: {countNormal}</div>
        <div>重たい処理: {heavyFunction(countHeavy)}</div>
        <div>メモ化した処理: {heavyFunctionMemo}</div>
      </div>
      <div className="app-button">
        <button onClick={() => setCountNormal(countNormal + 1)}>通常+1</button>
        <button onClick={() => setCountHeavy(countHeavy + 1)}>重たい+1</button>
      </div>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

useRef

useRef は元々あった createRef の hooks 版です。 その名の通り、DOMに対する参照を持つために使われるのが主な目的です。 ですがそれ以外の用途にも利用することができます。

const App = () => {
  const inputEl = React.useRef(null);
  const [text, setText] = React.useState("");
  const handleClick = () => {
    setText(inputEl.current.value);
  };
  console.log("描画!!");
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={handleClick}>set text</button>
      <p>テキスト : {text}</p>
    </>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));

useImperativeHandle

useImperativeHandle は Hooks のひとつで、関数コンポーネントにメソッドを追加し、それを親コンポーネントから使えるようにするための機能。 使う場所は、他の Hooks と同様に、関数コンポーネントのトップレベル。 

// 公開したいメソッドを持つコンポーネントの定義
// プロパティを追加する場合は、forwardRef<Handler, Props>と定義する。
const FancyInput = React.forwardRef((props, ref) => {
  const inputRef = React.useRef({});

  React.useImperativeHandle(ref, () => {
    return {
      setFocus() {
        inputRef.current.focus();
      }
    };
  });

  return <input ref={inputRef} type="text" />;
});

const App = () => {
  const ref = React.useRef({});
  return (
    <>
      <FancyInput ref={ref} />
      <button
        onClick={() => {
          ref.current.setFocus();
        }}
      >
        click
      </button>
    </>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));

useLayoutEffect

useEffect と同一ですが、DOM の変更があった後で同期的に副作用が呼び出されます。これは DOM からレイアウトを読み出して同期的に再描画を行う場合に使ってください。useLayoutEffect の内部でスケジュールされた更新はブラウザによって描画される前のタイミングで同期的に処理されます。


//待ち時間の設定
const sleep = (waitMsec) => {
  var startMsec = new Date();
  while (new Date() - startMsec < waitMsec);
}

const App = () => {
  const [text, setText] = React.useState("")
  //画面描画前に実行
  React.useLayoutEffect(() => {
      sleep(5000);                  //5秒待
      setText("表示されました!")    //テキストの設定
    });

    return <p>{`text:${text}`}</p>
}

ReactDOM.render(<App />, document.getElementById("root"));

useDebugValue

useDebugValue を使って React DevTools でカスタムフックのラベルを表示することができます。

const useRenderCount = () => {
  const renderCountRef = React.useRef(0);

  React.useDebugValue(
    `このコンポーネントは${renderCountRef.current}回再描画されました`,
  );

  React.useEffect(() => {
    renderCountRef.current++;
  });
};
const App = () => {
  useRenderCount();

  return <p>このコンポーネントを開発者ツールで見ると再描画数が表示されます</p>;
};

ReactDOM.render(<App />, document.getElementById("root"));

コメントを残す

入力エリアすべてが必須項目です。メールアドレスが公開されることはありません。

内容をご確認の上、送信してください。