react-hooksでreduxを使う
案件でreactの改修が起きそうなので、react,redux,typescript,react-routerで気持ちよく書きたいと思い殴り書きだがサンプルを作って試してみました。
作ったリポジトリ
ざっくり解説
import React from 'react'
import { BrowserRouter, Route, Link } from 'react-router-dom'
import App from './App'
import Counter from './counter'
const Router: React.FC = () => (
<BrowserRouter>
<nav>
<ul>
<li><Link to="/">HOME</Link></li>
<li><Link to="/counter">COUNTER</Link></li>
</ul>
</nav>
<hr/>
<div className="content">
<Route exact path="/" component={ App } />
<Route path="/counter" component={ Counter } />
</div>
</BrowserRouter>
)
export default Router
ここは特に工夫していないです。ナビゲーションも適当。
import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { RootState, incrementActionCreator, decrementActionCreator } from './store';
const Counter: React.FC = () => {
const [count, setCount] = useState(0)
const dispatch = useDispatch()
const counterState = useSelector((state: RootState) => state.counter)
return (
<div>
react hooks count { count }
<div>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
redux count { counterState.count }
<div>
<button onClick={() => dispatch(incrementActionCreator())}>+</button>
<button onClick={() => dispatch(decrementActionCreator())}>-</button>
</div>
</div>
)
}
export default Counter
react-hooksもそんな使ってないのでちょっと余計なコードも追加。
useSelector
前までの mapStateToProps
の代わり
https://react-redux.js.org/next/api/hooks#useselector
useDispatch
storeのdispatchを生成してくれる。アクションの中身を指定して渡してあげる。useCallbackは一旦省略。
https://react-redux.js.org/next/api/hooks#usedispatch
store
import { combineReducers, createStore } from 'redux'
// Counter State
interface CounterState {
count: number
}
const counterInitialState: CounterState = {
count: 0
}
// actions
export const INCREMENT = 'COUNTER/INCREMENT'
export const DECREMENT = 'COUNTER/DECREMENT'
interface IncrementAction {
type: typeof INCREMENT
payload: null
}
interface DecrementAction {
type: typeof DECREMENT
payload: null
}
type CounterActionTypes = IncrementAction | DecrementAction
export const incrementActionCreator = (): CounterActionTypes => {
return {
type: INCREMENT,
payload: null
}
}
export const decrementActionCreator = (): CounterActionTypes => {
return {
type: DECREMENT,
payload: null
}
}
//---
// reducers
export const counterReducer = (
state = counterInitialState,
action: CounterActionTypes
): CounterState => {
switch (action.type) {
case INCREMENT:
return { count: state.count + 1 }
case DECREMENT:
return { count: state.count - 1 }
default:
return state
}
}
//---
// combine
export const rootReducer = combineReducers({
counter: counterReducer
})
export type RootState = ReturnType<typeof rootReducer>
export const store = createStore(rootReducer)
ReturnType
知らなかった。
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#example-4
感想
ここにreset用のアクションとか付け足したいけど省略。最近vuexばっかり使っていたので、比較でもないですが僕はやっぱりreactの方が好きだなと思った。
参考
https://react-redux.js.org/next/api/hooks
https://redux.js.org/recipes/usage-with-typescript
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html
https://qiita.com/seya/items/8291f53576097fc1c52a