LoginSignup
6
8

More than 3 years have passed since last update.

react-hooksでreduxを使う

Last updated at Posted at 2019-08-19

react-hooksでreduxを使う

案件でreactの改修が起きそうなので、react,redux,typescript,react-routerで気持ちよく書きたいと思い殴り書きだがサンプルを作って試してみました。

作ったリポジトリ

ざっくり解説

router.tsx
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

ここは特に工夫していないです。ナビゲーションも適当。

counter.tsx
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

store.ts
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

6
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8