LoginSignup
2
1

[自分用]Next.jsのカスタムフックとリフトアップ

Last updated at Posted at 2023-05-21

カスタムフック

カスタムフックを使う理由はコンポーネント分けと同じで、use系も使い回しができるようになる。
コンポーネント分けするよりもUIの自由度が高い!

カスタムフックを使用するときに気をつけること

・hooksの命名はuseから始めるべき(マニュアルにそう書いてある)。hooksと他の関数を見分けやすくするため。
・hooksはrerutnより前に記述する。
・hooksはJavaScriptの関数ではなく、Reactにおけるコンポーネント内で呼び出す。

カスタムフックの利用方法

useBgLightBlue.jsx
import { useEffect } from 'react';

export const useBgLightBlue = () => {
  useEffect(() => {
    console.log('こいつが実行されるよ');
    return () => {
      console.log('アンマウントも実行されるよ');
    }
  }, []);
}

useInputArray.jsx
import { useCallback, useState } from 'react';

export const useInputArray = () => {
  const [text , setText] = useState("");
  const [array, setArray] = useState([1, 2, 3]);

  const handleChange = useCallback((e) => {
    setText(e.target.value);
  }, []);

  const handleAdd = useCallback(() => {
    setArray((array) => {
      if (array.some((item) => item === text)) {
        alert("既に存在しています");
        return array;
      }
      return [...array,text];
    });
  }, [text])

  return { text , array , handleChange , handleAdd};
}

useCounter.jsx
import { useCallback, useState } from 'react';

export const useCounter = () => {
  const [foo, setFoo] = useState(1);
  const [isShow, setIsShow] = useState(true);

  const handleClick = useCallback(() => {
    setFoo(foo => foo + 1);
  },[]);

  const handleDisplay = useCallback(() => {
    setIsShow((isShow)=> !isShow);
  }, []);

  return { foo , isShow , handleClick , handleDisplay};
}

上記三つのコンポーネントが機能ごとにhooks分けされたもの。

index.jsx
import { useCounter } from 'src/hooks/useCounter';
import { useInputArray } from 'src/hooks/useInputArray';
import { useBgLightBlue } from 'src/hooks/useBgLightBlue';

export default function Home() {
  const { foo, isShow, handleClick, handleDisplay } = useCounter();
  const { text, array, handleChange , handleAdd } = useInputArray();
  useBgLightBlue();

  return (
    <>
      {isShow ? <h1>{foo}</h1> : null}
      <button
        onClick={handleClick}>
        ボタン
      </button>
      <button
        onClick={handleDisplay}
      >
        {isShow ? "非表示" : "表示"}
      </button>
      <input type="text" value={text} onChange={handleChange} />
      <button onClick={handleAdd}>追加</button>
      <ul>
        {array.map((item) => {
          return <li key={item}>{item}</li>;
        })}
      </ul>
    </>
  )
}

この三つのコンポーネントをindex.jsxで読み込む。

なぜコンポーネント分けではなく、カスタムフックを使用するのか

それはカスタムフックの方がUIの自由度が高いため。

コンポーネントのようにUlで分けている分けではなく、あくまでも機能で分けているている。
そのため使い回したいがUlが一致していないときにカスタムフックが役に立つ。

だから「どこにこの機能を付与してもUIは統一!!」という場合はコンポーネント分けしてもいいのかなと思う。
(カスタムフックの方が「どんな機能を持っているのか」は把握しやすくなるけどね)

リフトアップ

上記でuse系をさまざまなファイルで使用できるようになったけど、状態の維持はできない。
例えばカウンターで5にしたけど、ページ遷移をしたら1に戻ってしまう。理由はconst [foo, setFoo] = useState(1)が再度読み込まれてしまい、初期値に戻るから。

そこで利用するのが_app.jsx!
_app_jsxでpropsとして渡したものは各ページでpropsとして受け取れる。
これを利用して、状態を複数ページで共有することができる。

_app.jsx
import '@/styles/globals.css'
//インポート
import { useCounter } from 'src/hooks/useCounter';
import { useInputArray } from 'src/hooks/useInputArray';
import { useBgLightBlue } from 'src/hooks/useBgLightBlue';

export default function App({ Component, pageProps }) {
  //useCounterで設定した変数や初期値をcunterに代入。
  const counter = useCounter();
  const inputArray = useInputArray();
  useBgLightBlue();

  return (
    //counterの中身を展開してpropsとして渡す
    <Component {...pageProps} {...counter} {...inputArray} />
  )
}

index.jsx
export default function Home(props) {
  //オブジェクトを分割代入しているので、順不同。
  const { count, isShow, handleClick, handleDisplay,text, array, handleChange , handleAdd } = props;
2
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
2
1