LoginSignup
67
59

More than 3 years have passed since last update.

React Hooks(useState | useReducer) + TypescriptでサンプルWebアプリ

Last updated at Posted at 2020-01-12

久しぶりにReact触りました。以前Reduxを使ったサンプルアプリを作りましたが、もっと手軽に作りたいと思いますので、今回は同様のアプリをReact Hooksで作成したいと思います。

以前のサンプルはこちら

サンプルコードを書く前にReact Hooksの簡単なご紹介を。
React HooksはReact 16.8から追加された新しい機能です。

Reactの特徴は再利用可能なコンポーネントが作成可能であることですが、ステートフルなコンポーネントはコンポーネント間での再利用が難しく、ステートの複雑なライフサイクルにより理解が困難なものとなっていました。
Hookの導入により1つのコンポーネントを複数の小さな関数に分割可能にし、よりシンプルなコンポーネントを作ることが可能となります。

Hookの説明については公式を参照してください。

Hooksの説明

Hooksにはいくつかのフックが用意されています。

  • useState:関数コンポーネントの中でローカルな state を使うために呼び出す。
  • useEffect:関数コンポーネント内で副作用を実行することを可能にする。(componentDidMount, componentDidUpdate および componentWillUnmount と同様の目的で使う。)
  • useContext:React のコンテクストをコンポーネントのネストなしに利用できる
  • useReducer:複雑なコンポーネントのローカル state をリデューサ (reducer) を用いて管理できる

など

すべてのフックについては公式 Hooks API リファレンスを参照してください。

コードを書く

では、早速コードを書きます。

create-react-appで最初に作成されたテンプレートのApp.tsxを修正します。(Reduxのサンプルと違いindex.tsxの修正は不要です。)

src/App.tsx
import React from 'react';
import './App.css';
import { TopPage } from './components/TopPage'

const App: React.FC = () => {
  return (
    <>
      <TopPage />
    </>
  );
}

export default App;

useStateを使って書く

TopPageコンポーネントをuseStateフックを使って実装します。
(TextInput、RadioInput、SubmitButton、ShowStateは以前のサンプルを流用しています。)

src/components/TopPage.tsx
import React, { useState } from 'react'
import { TextInput } from './TextInput'
import { RadioInput } from './RadioInput';
import { SubmitButton } from './SubmitButton';
import { ShowState } from './ShowState';

export const TopPage: React.FC = () => {
    const [inputValue, setInputValue] = useState('')
    const [selectedValue, setSelectedValue] = useState('')
    const [clickCount, setClickCount] = useState(0)
    const handleInputValue = (value: string) => {
        setInputValue(value);
        console.log(value)
    }

    const handleSelectedValue = (value: string) => {
        setSelectedValue(value)
    }

    const submit = () => {
        setClickCount(clickCount + 1)
    }
    return (
        <>
            <TextInput title='入力' inputValue={inputValue} onChangeValue={handleInputValue} />
            <RadioInput title='ラジオ' selectedValue={selectedValue} onChangeValue={handleSelectedValue} />
            <SubmitButton title='Click me' onClick={submit} />
            <ShowState inputValue={inputValue} selectedValue={selectedValue} clickCount={clickCount} />
        </>
    )
}

Webアプリを起動すると以下のような画面が表示されます。

image.png

useReducerを使って書く

次に同じ内容をuseReducerを使って書き直してみます。
useReducerはuseStateの代替品です。公式によると「通常、useReducer が useState より好ましいのは、複数の値にまたがる複雑な state ロジックがある場合や、前の state に基づいて次の state を決める必要がある場合です」とのこと。

では、早速書き換えてみます。

TopPage.tsx
import React, { useReducer } from 'react'
import { TextInput } from './TextInput'
import { RadioInput } from './RadioInput';
import { SubmitButton } from './SubmitButton';
import { ShowState } from './ShowState';

interface TopPageState {
    inputValue: string,
    selectedValue: string,
    clickCount: number,
}
interface TopPageAction {
    type: ActionType,
    payload: TopPageState,
}
enum ActionType {
    ACTION_INPUT_TEXT = 'ACTION_INPUT_TEXT',
    ACTION_SELECT_RADIOBUTTON = 'ACTION_SELECT_RADIOBUTTON',
    ACTION_CLICK_BUTTON = 'ACTION_CLICK_BUTTON',
}

const initialState = { inputValue: '', selectedValue: '', clickCount: 0 }

const reducer: React.Reducer<TopPageState, TopPageAction> = (state: TopPageState, action: TopPageAction) => {
    switch (action.type) {
        case ActionType.ACTION_INPUT_TEXT:
            return {
                ...state,
                inputValue: action.payload.inputValue
            }
        case ActionType.ACTION_SELECT_RADIOBUTTON:
            return {
                ...state,
                selectedValue: action.payload.selectedValue
            }
        case ActionType.ACTION_CLICK_BUTTON:
            return {
                ...state,
                clickCount: action.payload.clickCount
            }
        default:
            throw new Error()
    }
}

export const TopPage: React.FC = () => {
    const [state, dispatch] = useReducer(reducer, initialState)

    const handleInputValue = (value: string) => {
        dispatch({ type: ActionType.ACTION_INPUT_TEXT, payload: { ...state, inputValue: value } });
        console.log(value)
    }

    const handleSelectedValue = (value: string) => {
        dispatch({ type: ActionType.ACTION_SELECT_RADIOBUTTON, payload: { ...state, selectedValue: value } });
    }

    const submit = () => {
        dispatch({ type: ActionType.ACTION_CLICK_BUTTON, payload: { ...state, clickCount: state.clickCount + 1 } });

    }
    return (
        <>
            <TextInput title='入力' inputValue={state.inputValue} onChangeValue={handleInputValue} />
            <RadioInput title='ラジオ' selectedValue={state.selectedValue} onChangeValue={handleSelectedValue} />
            <SubmitButton title='Click me' onClick={submit} />
            <ShowState inputValue={state.inputValue} selectedValue={state.selectedValue} clickCount={state.clickCount} />
        </>
    )
}

さっくりとReduxライクに書き換えできました。

React Hooksを利用することで非常に簡単でシンプルなコードが書けましたね。
React Hooksの登場で、従来鉄板だと思われていたReact + Reduxの使い所も変わって来そうですね。

67
59
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
67
59