react hooks+redux toolkitを使ってtodoアプリを作ってみた

この記事はTOWN株式会社アドベントカレンダー6日目の記事です（大遅刻して申し訳ないです。。。)
https://adventar.org/calendars/4335

React HooksとはReact16.8から追加された機能で、関数コンポーネントにstateなどの機能を使えるようになった記法です。
https://ja.reactjs.org/docs/hooks-intro.html

Redux toolkitとはどうしても複雑な記述を書かなくてはいけないイメージが(個人的に)あるreduxを簡潔に書くことができるライブラリです。reduxの公式チームがリリースしているそうです。
https://github.com/reduxjs/redux-toolkit

今回はreact hooksとredux toolkitを使ってtodoアプリを作成してみました。

こちらが完成形です。
![reduxsample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165984/4d7f88f6-21cf-e739-a87c-91854e6b02eb.gif)

#アプリの作成
まず`create-react-app`を使ってアプリのテンプレートを作成します。

```
npx create-react-app hooks-toolkit-sample
```

パッケージをインストールします。

```
yarn add react-redux @reduxjs/toolkit
```

or

```
npm i react-redux @reduxjs/toolkit
```

`yarn start`もしくは`npm start`でアプリを起動します。reactのロゴsvgなどのいらないファイルやいらない記述等は削除しておきます。

#actionとreducerの作成
redux toolkitのcreateSliceを使用し、actionとreducerを同時に作成しています。このおかげでactionとreducerを別々のディレクトリに分ける必要がなくなりました。

```modules/listModule.js
import { createSlice } from "@reduxjs/toolkit";

const listModule = createSlice({
    name: "list",
    initialState: [],
    reducers: {
      addList: (state, action) => [...state, action.payload],
      deleteList: (state, action) => state.filter(el => el.id !== action.payload),
    }
});

export default listModule;
```

#storeの作成
reducerとmiddlewareを登録します。先ほどのmoduleのreducerをredux toolkitの`combineReducers`を使って登録しています。

```src/store.js
import { combineReducers, configureStore } from "@reduxjs/toolkit";
import listModule from "./modules/listModule";

const rootReducer = combineReducers({
    list: listModule.reducer,
});

export const setupStore = () => {
    const store = configureStore({
        reducer: rootReducer,
    });
    return store
}

```

#コンポーネントの作成
まず`useDispatch()`を使ってactionが使えるようにします。

todoを追加するためのテキストのinputの操作ですが、inputの`onChange`に`handleChangeList`関数を設定して文字を入力するたびに関数が実行されるようにします。
そしてreact hooksの`useState`を用い、入力したテキストを一時的に`newListName`のstateに入れておきます。
`addList`ボタンを押した瞬間に`newListName`を引数にしてactionの`addList`を実行し、storeのstateにtodoのテキストが入ることで画面が更新されます。

```jsx:components/App.js
import React from 'react';
import { useState } from 'react';
import { useDispatch, useSelector } from "react-redux";
import listModule from "../modules/listModule";
import List from "./List.js";

const App = () => {
  const lists = useSelector(state => state.list);

  const [newListName, setListName] = useState("");
  const dispatch = useDispatch();

  const addList = () => {
    if (newListName !== "") {
      dispatch(listModule.actions.addList({id: lists.length > 0 ? lists.reduce((a,b) => a.id > b.id ? a : b).id + 1 : 1, title: newListName}));
    }
    setListName("");
  };

  const handleChangeList = (e) => {
    setListName(e.target.value);
  };

  return (
    <div className="App">
      <p>Redux TODO sample</p>
      {lists.map((list) =>
        <List key={list.id} item={list} />
      )}
      <input type="text" onChange={handleChangeList} value={newListName}/>
      <button onClick={addList}>addList</button>
    </div>
  );
};

export default App;

```

```jsx:components/List.js
import React from 'react';
import { useState } from 'react';
import { useDispatch, useSelector } from "react-redux";
import listModule from "../modules/listModule";

const List = (props) => {
  const { item } = props;
  const dispatch = useDispatch();

  const deleteList = () => dispatch(listModule.actions.deleteList(item.id));

  return (
    <div>
      {item.title}
      <button onClick={deleteList}>deleteList</button>
    </div>
  )
}

export default List;

```

ここまでクラスコンポーネントを使わず、かつとてもコンパクトにreduxのアプリを作成することが出来ました。書いていてかなり楽しかったので今度はもう少し複雑なアプリも作ってみようかと思います。

参考:
TypescriptとReact HooksでReduxはもうしんどくない
https://qiita.com/ky7ieee/items/b3f43ecc497b9115449a
Redux Hooks によるラクラク dispatch & states
https://qiita.com/Ouvill/items/569384e5c8c7ce78f98e
Redux の記述量多すぎなので、 Redux の公式ツールでとことん楽をする。 ( Redux Toolkit)
https://qiita.com/Ouvill/items/a76e9cbce569d01f2931