BLOG

React/Custom hookについて

独自のフック、Custom hookを作成し、ロジックを再利用できるようにする。

クリックごとにドキュメントタイトルを更新するコンポーネントを作成

・src/components/DocTitleUpdateOne.jsを作成

・rfceスニペット

・useState,useEffectをReactからインポート

・useStateを使ってカウントステートを作成(初期値には0をセット)

・カウントアップボタンをjsx内に作成

・onClickイベントに更新ロジックを追加

・useEffectを使ってカウントをドキュメントタイトルに反映する。

・初回描画時、カウントの更新が行われた際にタイトルの更新を行いたいので、dependenciesリストにはカウントを渡す。

・App.jsに読み込む

DocTitleUpdateOne.js

import React,{useState, useEffect} from 'react'

function DocTitleUpdateOne() {
    const [count, setCount] = useState(0)
    useEffect(() => {
        document.title = `カウント${count}`
    },[count])
    return (
        <div>
            <button onClick={() => setCount(count + 1)}>count - {count}</button>
        </div>
    )
}

export default DocTitleUpdateOne

App.js

import React, {useReducer, useEffect} from 'react';
import './App.css';
import DocTitleUpdateOne from './components/DocTitleUpdateOne';



function App() {

  return (
    <div className="App">
    <h1>Custom hook</h1>
      <DocTitleUpdateOne />
    </div>
  );
}

export default App;

ボタンを押したら、ボタン内とタイトルがカウントアップされるアプリが表示される。

次にUpdateOneと同様の機能を持つUpdateTwoのコンポーネントを作成する。

・src/components/DocTitleUpdateTwo.jsを作成

・UpdateOneからソースをコピー

・コンポーネント名を変更

・これをApp.jsに読み込む

DocTitleUpdateTwo.js

import React,{useState, useEffect} from 'react'

function DocTitleUpdateTwo() {
    const [count, setCount] = useState(0)
    useEffect(() => {
        document.title = `カウント${count}`
    },[count])
    return (
        <div>
            <button onClick={() => setCount(count + 1)}>count - {count}</button>
        </div>
    )
}

export default DocTitleUpdateTwo

App.js

import React, {useReducer, useEffect} from 'react';
import './App.css';
import DocTitleUpdateOne from './components/DocTitleUpdateOne';
import DocTitleUpdateTwo from './components/DocTitleUpdateTwo';



function App() {

  return (
    <div className="App">
    <h1>Custom hook</h1>
      <DocTitleUpdateOne />
      <DocTitleUpdateTwo />
    </div>
  );
}

export default App;

同じ機能のボタンが2つ並んで表示される。

この更新処理をカスタムフックを作成して再利用していく。

・componentsフォルダと同階層にhooksフォルダを用意し、useDocumentTitleファイルを用意する。

※カスタムフック用にファイルを作成する際、useというプレフィックスをつけることを意識する。

・src/hooks/useDocumentTitle.jsにてrfceスニペット

このコンポーネントの中に先程の共通処理を移していく

・useEffect内で行っている、DocumentTitleの更新処理が重複されているので、これをuseDocumentTitleコンポーネントに移していく

※returnのjsxは必要ないので削除する。

・Reactの代わりにuseEffectをインポートする。

・今作成したカスタムフックコンポーネントをそれぞれのコンポーネントで読み込む

・引数にcountを渡し、countの受け渡しを行う。

・useDocumentTitle.js内、propsにcountを追加

・UpdateOneコンポーネントでもカスタムフックを読み込む(useDocumentTitle(count))

useDocumentTitle.js

import {useEffect} from 'react'

function useDocumentTitle(count) {
    useEffect(() => {
        document.title = `カウント${count}`
    },[count])
}

export default useDocumentTitle

DocTitleUpdateOne.js

import React,{useState, useEffect} from 'react'
import useDocumentTitle from '../hooks/useDocumentTitle'


function DocTitleUpdateOne() {
    const [count, setCount] = useState(0)
    useDocumentTitle(count)
    return (
        <div>
            <button onClick={() => setCount(count + 1)}>count - {count}</button>
        </div>
    )
}

export default DocTitleUpdateOne

DocTitleUpdateTwo.js

import React,{useState, useEffect} from 'react'
import useDocumentTitle from '../hooks/useDocumentTitle'

function DocTitleUpdateTwo() {
    const [count, setCount] = useState(0)
    useDocumentTitle(count)
    return (
        <div>
            <button onClick={() => setCount(count + 1)}>count - {count}</button>
        </div>
    )
}

export default DocTitleUpdateTwo

これで同じようにタイトルとボタン内がカウントアップされるボタンが2つ並んだアプリが実装できる。

カスタムフックは2つのJavascriptの関数間でロジックを共有したいときに別の関数を用意し、そこに共通処理を書くのと同じで、フックを使うことで、共通ロジックをまとめて書くことができ別々のコンポーネントで利用することが出来る。

フォームを作成し、共通処理をカスタムフックに実装していく。

・src/components/Form.jsを作成する。

・rfceスニペット

・re

・ReactからuseStateをインポート

・firstName, lastNameを扱うのでそれらのステートを作成。

・初期値にemptyStringを渡す。

・jsx内にformを用意する。

・それぞれのインプットフィールドにvalueとonChangeメソッドを追加していく

・App.jsに読み込む

Form.js

import React,{useState} from 'react'

function Form() {
    const [firstName, setFirstName] = useState('')
    const [lastName, setLastName] = useState('')
    return (
        <div>
            <form>
                <div>
                    <label>First Name</label>
                    <input
                     type="text"
                     value={firstName}
                     onChange={e => setFirstName(e.target.value)} />
                </div>
                <div>
                    <label>Last Name</label>
                    <input
                     type="text"
                     value={lastName}
                     onChange={e => setLastName(e.target.value)} />
                </div>
            </form>
        </div>
    )
}

export default Form

App.js

import React, {useReducer, useEffect} from 'react';
import './App.css';
import Form from './components/Form'



function App() {

  return (
    <div className="App">
      <h1>Custom hook</h1>
      <Form />
    </div>
  );
}

export default App;

フォームの表示と入力が確認できる。

次にSubmitボタンを実装。

・フォームの送信にはonSubmitボタンを使う。

送信時の処理は一旦preventDefaultでのページのリフレッシュ防止、アラートにて入力した値の表示をする。

Form.js

import React,{useState} from 'react'

function Form() {
    const [firstName, setFirstName] = useState('')
    const [lastName, setLastName] = useState('')
    const handleSubmit = e => {
        e.preventDefault()
        alert(`ようこそ ${firstName} ${lastName}`)
    }
    return (
        <div>
            <form onSubmit={handleSubmit}>
                <div>
                    <label>First Name</label>
                    <input
                     type="text"
                     value={firstName}
                     onChange={e => setFirstName(e.target.value)} />
                </div>
                <div>
                    <label>Last Name</label>
                    <input
                     type="text"
                     value={lastName}
                     onChange={e => setLastName(e.target.value)} />
                </div>
                <button type="submit">送信</button>
            </form>
        </div>
    )
}

export default Form

送信時の処理の実装が完了したのでカスタムフックを作成していく。

インプットフィールドでの値のバインドと、onChangeイベントの振る舞いを持ったカスタムフックを作成する。

・src/hooks/useInput.jsを作成

・rfceスニペット

・Reactを削除しuseStateをインポート

・インプットでの入力値をステートで管理するためにvalueステートを作成。

・初期値にはuseInputの読み込み先から渡される値をセットする。

・次にフォームの値を初期値に戻すreset関数を作成。関数での処理はsetValueを使い、initialValueを使うだけで良い

・インプットフィールドに渡す、オブジェクトを作成する。オブジェクト内にはまず入力値のvalue、onChangeメソッドを追加していく。onChange内では受け取った値をvalue値にセットする。

・返り値にはjsxではなくvalue値、bind、reset関数を渡す。

それでは今作成したuseInputフックを使って、formコンポーネントを書き換えていく。

・まずはuseInputをインポートし、引数にemptyStringを渡す。そしてuseInputから返される返り値をそれぞれの変数に代入していく。

・lastNameも同様に宣言。

先程まで管理していたステートがuseInputに移されたのでそれぞれ削除する。

const [firstName, setFirstName] = useState(”)

const [lastName, setLastName] = useState(”)

・インプットフィールドに渡されているそれぞれのアトリビュートをuseInputで作成したbindオブジェクトに置き換える。

・まずはbindFirstName。bind関数内にはvalue、onChangeメソッドが含まれているので、これらをスプレッド構文を使って展開して渡していく。

・Submit関数内にそれぞれresetファンクションを追加してページを確認。

useInput.js

import {useState} from 'react'

function useInput(initialValue) {
    const[value, setValue] = useState(initialValue)
    const reset = () => {
        setValue(initialValue)
    }
    const bind = {
        value,
        onChange: e => {
            setValue(e.target.value)
        }

    }
    return [value, bind, reset]
}

export default useInput

Form.js

import React,{useState} from 'react'
import useInput from '../hooks/useInput'


function Form() {

    const [firstName, bindFirstName, resetFirstName] = useInput('')
    const [lastName, bindLastName, resetLastName] = useInput('')


    const handleSubmit = e => {
        e.preventDefault()
        alert(`ようこそ ${firstName} ${lastName}`)
        resetFirstName()
        resetLastName()
    }
    return (
        <div>
            <form onSubmit={handleSubmit}>
                <div>
                    <label>First Name</label>
                    <input
                     type="text"
                     {...bindFirstName} />
                </div>
                <div>
                    <label>Last Name</label>
                    <input
                     type="text"
                     {...bindLastName} />
                </div>
                <button type="submit">送信</button>
            </form>
        </div>
    )
}

export default Form

これでインプットフィールドに入力して送信すると、アラートで入力した内容が表示され、リセットされるようになる。