6
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?

More than 1 year has passed since last update.

【React】そのカスタムフック、作らなくていいよ。react-useがあれば。

Posted at

はじめに

2022年現在、Reactを使うとなると、hooksを使う頻度が高いかと思います。
ステートの保持やAPI通信などを伴う副作用の注入。

そしてそれらを汎用化したり、処理を切り分けるために実装するカスタムフック。

今回はそのカスタムフック、実はreact-useというライブラリを使えば作らなくていいかもよ。
という話をします。

react-useとは

よく使う汎用的なhooksの処理をライブラリとして提供してくれています。
今回はよく使うものにフォーカスを当てて話しますが、他にも色々あるので、是非Githubをじっと眺めてみてください。

useEffectOnce

通常、useEffectはコンポーネントが再レンダリングされた際も動きますが、それを初回マウント後と初回アンマウント後に一度のみ行いたい場合に使います。

import {useEffectOnce} from 'react-use';

const Demo = () => {
  useEffectOnce(() => {
    console.log('Running effect once on mount')

    return () => {
      console.log('Running clean-up of effect on unmount')
    }
  });

  return null;
};

実は、このコードはReactの公式サイトにも記述があります。

もしも副作用とそのクリーンアップを 1 度だけ(マウント時とアンマウント時にのみ)実行したいという場合、空の配列 ([]) を第 2 引数として渡すことができます。こうすることで、あなたの副作用は props や state の値のいずれにも依存していないため再実行する必要が一切ない、ということを React に伝えることができます。これは特別なケースとして処理されているわけではなく、依存配列を普通に処理すればそうなるというだけの話です。

なので、公式通り、実際のコードをみてみるとuseEffectをラップして空配列を依存配列に渡しているだけだったりします。

useEffectOnece.ts
import { EffectCallback, useEffect } from 'react';

const useEffectOnce = (effect: EffectCallback) => {
  useEffect(effect, []);
};

export default useEffectOnce;

これだけのhooksを使う必要があるのか、という考え方もあるかもしれませんが、

  • 明示的に一度しか実行されないことがわかる
  • テストコードを書く必要がない

という点で良いhooksだなと思います。

useToggle

モーダルを実装する際など、toggle的なステートを管理するのにuseStateuseReducerを用いた実装が上がってくる時があります。

でも、useToggleを使えばシンプルに書けます

import { useToggle } from 'react-use';

const Demo = () => {
  const [on, toggle] = useToggle(true);

  return (
    <div>
      <div>{on ? 'ON' : 'OFF'}</div>
      <button onClick={toggle}>Toggle</button>
      <button onClick={() => toggle(true)}>set ON</button>
      <button onClick={() => toggle(false)}>set OFF</button>
    </div>
  );
};

useAsync

非同期処理ってちょっと書くの面倒だったりするんですよね。
可読性も悪いですし。
useAsyncを使うといいです。async関数を引数に取ることができます。

import {useAsync} from 'react-use';

const Demo = ({url}) => {
  const state = useAsync(async () => {
    const response = await fetch(url);
    const result = await response.text();
    return result
  }, [url]);

  return (
    <div>
      {state.loading
        ? <div>Loading...</div>
        : state.error
          ? <div>Error: {state.error.message}</div>
          : <div>Value: {state.value}</div>
      }
    </div>
  );
};

useMount

これまたシンプルなhooksですが、マウント時に実行したい関数を渡すことができます。

import {useMount} from 'react-use';

const Demo = () => {
  useMount(() => alert('MOUNTED'));
  return null;
};

createReducerContext

Contextを用いた実装をしたくなる時があると思いますが、毎回同じようなコードを書いてるな、、と思います。
このcreateReducerContextでは、reducerと初期値を渡せば、そのreducerとProviderを返してくれます。

import { createReducerContext } from 'react-use';

type Action = 'increment' | 'decrement';

const reducer = (state: number, action: Action) => {
  switch (action) {
    case 'increment':
      return state + 1;
    case 'decrement':
      return state - 1;
    default:
      throw new Error();
  }
};

const [useSharedCounter, SharedCounterProvider] = createReducerContext(reducer, 0);

だいたいContextを使うときは何かしらのステートとそれを更新するdispatch関数を返したいだけだったりするので、スマートにかけていいですね

まとめ

今回は自分で作っちゃいがちなカスタムフック、実はreact-useというライブラリがもうすでに作ってくれてるかもよ
という話をしました。

メンテナンスコストも下がり、テストコードを書く量も減る。是非使ってみてください

6
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
6
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?