Reduxのthunkは割と鬼門だと思っていて、今回redux-toolkitを勉強するにあたって公式docを読んだのですが、割と分からなかったので比較とメモを兼ねて書いてみました。
Reduxで書く場合
ちょっと面倒くさかったので、1ファイルにかなりまとめて書いてしまいましたが、reduxの方はthunk
でAPIを呼ぶ関数を関数で包んでるだけで(ここの部分はまだ勉強中)、単純にload時にAPIを呼ぶ関数内でfetchした値をactionディスパッチで送信しているだけです。
composeEnhancers
などを使っているのは、ChromeのRedux用ツールで可視化するためだけです。
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();
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を使う場合
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();
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
などにも慣れず公式だとたくさん情報が書いてあるのですがシンプルに使ってみたいだけの時になかなかうまくいかなかったので、今回書いてみました。
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;