[初心者向け]React Redux Basic TutorialにあるTodoAppを今書き直したら

# はじめに

枠が空いていましたので、Advent Calendarに参加させていただきました。
10月からReactの学習を始めた新参者です。
間違っているところなどあればご指摘頂けると幸いです。

React Reduxを学ぼうと思った時に [Basic Tutorial | React Redux](https://react-redux.js.org/introduction/basic-tutorial) にあるTodoAppをみる方も多いと思います。
私はReactの学習を [くるみ割り書房 ft. React - BOOTH](https://oukayuka.booth.pm/) さんから出ているりあクト！シリーズで勉強しているのですが、改めてBasic TutorialのTodoAppを見るとこれはこのまま実務のコードとして参考に出来ないなと思いました。
例えば、`function TodoApp()`など関数でなく、アロー関数で書くなど。
これからReactでプロジェクトを進めていく時はおそらくどこもアロー関数になると思います。

これからReactを学ぶ上で最小限の小さいアプリを作っておいて見返すのは大事だと思いますので、せっかくならReact Redux Basic TutorialのTodoAppを書き直そうと思いました。

記事では、下記リポジトリの補足コメントを書いてるところが大きいです。
https://github.com/nori1994/todo-app-react

(追記)
2021/5にRedux公式ドキュメントが更新されてます。
ReduxのチュートリアルがRedux Toolkitを使って綺麗になっているのでこちらを見ればよさそうです。
https://redux.js.org/tutorials/quick-start
Reduxの学習リソースも増えてます。
https://redux.js.org/introduction/learning-resources#learning-resources
他にもFAQも増えたような気がします

# TodoApp
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">React Redux Basic TutorialにあるTodoAppを書き直してみた <a href="https://t.co/HZ0RZilcry">pic.twitter.com/HZ0RZilcry</a></p>&mdash; nori (@nori0__) <a href="https://twitter.com/nori0__/status/1335570702934495235?ref_src=twsrc%5Etfw">December 6, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

[Basic Tutorial | React Redux](https://react-redux.js.org/introduction/basic-tutorial) の全ての機能が揃っているわけでなく、最低限React Reduxとは？が分かる範囲で書いています。
そして、りあクト！の`11-redux/04-toolkit/src/features/todo.ts`で紹介されているSliceを実際に使うとどうなるのかを検証したかったためコードの参考引用しているところがあります。


## Slice 
Sliceは、Action と Reducerをまとめて定義できるためすっきり記述できます。

Action と Reducerとは？という方は下記にざっくりと書いたので参考になれば嬉しいです。
**Action**
Actionは、イベント、例えばStateをどう変化させるかを定義します。
ActionType(文字列)からActionCreatorと呼ばれる Actionを作る関数を生成します。

**Reducer**
Reducerは、以下のように古いStateとActionから新しいStateを取得するようになっています。  

```
type Reducer<S, A> = (state: S, action: A) => S
```
[Three Principles | Redux](https://redux.js.org/understanding/thinking-in-redux/three-principles)
Actionは、Dispatcherを介してReducterに渡されます。


`createSlice`を使うとActionTypeの定義とActionCreator、Reducerをまとめて定義できます。
今回のTodoAppにはTodoを管理する、表示内容をフィルターするの大きく2つの機能があると考えたので、それぞれSliceを生成して、`combineReducers`で1つのReducerにしました。
[createSlice | Redux Toolkit](https://redux-toolkit.js.org/api/createSlice)
[combineReducers | Redux](https://redux.js.org/api/combinereducers)  


例えば表示内容をフィルターする場合は、以下のようにAction と Reducerを定義しました。

```ts:visibilityFilterSlice.ts
export const visibilityFilterSlice = createSlice({
  name: 'visibilityFilter',
  initialState: {
    visibilityFilter: VISIBILITY_FILTER.INCOMPLETE,
  } as VisibilityFilterState,
  reducers: {
    changed: (state, action: PayloadAction<VISIBILTY_FILTER_TYPE>) => {
      const changedValue = action.payload;
      state.visibilityFilter = changedValue;
    },
  },
});
```

## Store
Storeは、アプリでの状態Stateを保管するためのものです。
`configureStore`でStoreを生成しています。
Stateの変化を履歴で表示してくれるデバックツール[Redux DevTools extension](https://github.com/zalmoxisus/redux-devtools-extension) がデフォルトでONになっているなど設定が優秀なので、`configureStore`でStoreを作っています。

`configureStore`などに含まれている内容は以下を参照。
[Redux Toolkit: Overview | Redux](https://redux.js.org/redux-toolkit/overview#whats-included)  


```tsx:index.tsx
const rootReducer = combineReducers({
  todo: todoSlice.reducer,
  visibilityFilter: visibilityFilterSlice.reducer,
});

const store = configureStore({ reducer: rootReducer });

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root'),
);
```

## Dispath
実際にStateを変更するにはDispatcherを介してReducterにActionが渡されることで変更されます。

```tsx:VisibilityFilters.tsx
const VisibilityFilters: FC = () => {
  const dispatch = useDispatch();
  const { changed } = visibilityFilterSlice.actions;
  const visibilityFilterState = useSelector(
    (state: RootState) => state.visibilityFilter,
  );

  return (
    <div>
      {Object.entries(VISIBILITY_FILTER).map(([_, currentFilter]) => {
        return (
          <text
            key={`visibility-filter-${currentFilter}`}
            className={`filter ' + ${
              currentFilter === visibilityFilterState.visibilityFilter
                ? 'filter--active'
                : ''
            }`}
            onClick={() => {
              dispatch(changed(currentFilter as VISIBILTY_FILTER_TYPE));
            }}
          >
            {currentFilter}
          </text>
        );
      })}
    </div>
  );
};
```

# まとめ
この小規模単位のアプリを手元に持っておけばこれどうなっているんだっけとなった時に振り返りやすくなったと感じています。
あと、[くるみ割り書房 ft. React - BOOTH](https://oukayuka.booth.pm/)さんで出ている本は旧版を除いて全て購入しました。TodoAppも多大に影響を受けています。とてもおすすめです。