Redux-Thunk vs createAsyncThunk(Redux-toolkit)

Reduxのthunkは割と鬼門だと思っていて、今回redux-toolkitを勉強するにあたって公式docを読んだのですが、割と分からなかったので比較とメモを兼ねて書いてみました。

###Reduxで書く場合

ちょっと面倒くさかったので、1ファイルにかなりまとめて書いてしまいましたが、reduxの方は`thunk`でAPIを呼ぶ関数を関数で包んでるだけで(ここの部分はまだ勉強中)、単純にload時にAPIを呼ぶ関数内でfetchした値をactionディスパッチで送信しているだけです。

`composeEnhancers`などを使っているのは、ChromeのRedux用ツールで可視化するためだけです。

```index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {Provider} from 'react-redux'
import {applyMiddleware, createStore, compose} from 'redux'
import thunk from 'redux-thunk'

const initState = {
  items: []
}

const rootReducer = (state = initState, action) => {
  switch(action.type) {
    case 'add': return {items: [...state.items, action.payload]}
    default: return state
  }
}



const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(rootReducer, /* preloadedState, */ composeEnhancers(
    applyMiddleware(thunk)
  ));

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

```App.js
import {useDispatch, useSelector} from 'react-redux'
import {useEffect} from 'react'

const addCreator = (data) => {
  return {type: 'add', payload: data}
}


function App() {
  const items = useSelector(state => state.items)
  const dispatch = useDispatch()

  const fetchData = () => {
    fetch('https://fakestoreapi.com/products')
    .then(res => res.json())
    .then(result => dispatch(addCreator(result)))
  }

  useEffect(()=> {
    fetchData()
  },[])
  return (
    <ul className="App">
      {!items ? (<p>Loading</p>):(items[0].map( i=> <li key={i.id}>{i.title}</li>))}
    </ul>
  );
}

export default App;
```

###Redux-toolkitを使う場合

```index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {Provider} from 'react-redux'
import {configureStore} from '@reduxjs/toolkit'
import cartReducer from './reducer';


const store = configureStore({
  reducer: cartReducer.reducer
})

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

reportWebVitals();
```

```App.js
import {useSelector, useDispatch} from 'react-redux'
import {useEffect} from 'react'
import { fetchCartList } from "./reducer";


function App() {
  const data = useSelector(state => state.cart.data)
  const dispatch = useDispatch()

  useEffect(() => {
    dispatch(fetchCartList())
  }, [])
  return (
    <div className="App">
      {data.length === undefined? (<p>Loading</p>):(data.map(i=><p>{i.title}</p>))}
    </div>
  );
}

export default App;
```

ここまでは大したことないですが、以下の`createAsyncThunk`のポイントは、toolkitでは`createAsyncThunk`で作成したAPI呼び出しの関数は自動的にredux側でactionを作成してくれ、それぞれ`pending`、`fulfilled`、`rejected`としてわざわざ自分でreducerを用意しなくても、`extraReducers`の中で各処理を書くことが出来るようです。

また、`createAsyncThunk`を使ったAPI呼び出しの関数を`useEffect`で呼んで処理することが出来てstoreで一緒に`applyMiddleware(thunk)`とする必要もありません。

とまあ、大した内容でもないのですが、個人的には割とこの`createSlice`などにも慣れず公式だとたくさん情報が書いてあるのですがシンプルに使ってみたいだけの時になかなかうまくいかなかったので、今回書いてみました。

```reducer.js
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"


const axios = require("axios");

export const fetchCartList = createAsyncThunk(
  "cart/cartItemLoading",
  () =>
    axios
      .get(`https://fakestoreapi.com/products`)
      .then((response) => response.data)
      .catch((error) => error)
);

const initialState = {
    cart: {
      status: "idle",
      data: {},
      error: {},
    },
  };
  
  const cartSlice = createSlice({
    name: "cart",
    initialState,
    reducers: {},
    extraReducers: {
      [fetchCartList.pending.type]: (state, action) => {
        state.cart = {
          status: "loading",
          data: {},
          error: {},
        };
      },
      [fetchCartList.fulfilled.type]: (state, action) => {
        state.cart = {
          status: "idle",
          data: action.payload,
          error: {},
        };
      },
      [fetchCartList.rejected.type]: (state, action) => {
        state.cart = {
          status: "idle",
          data: {},
          error: action.payload,
        };
      },
    },
  });
  
  export default cartSlice;
```