react-redux + typescriptでcountアプリ作成


react-redux + typescriptでcountアプリ作成

reactプロジェクト作成

npx create-react-app test-redux-count --typescript
// プロジェクト作成完了まで待つ
cd test-redux-count 

reduxインストール

typesyncを使って型定義もインストールします。

 yarn add react-redux redux && typesync && yarn

redux用のフォルダ構成作成

./src/store // reduxに関するファイルはここに
├── actionTypes.ts // actionTypeを作成
├── counter // 機能ごとにフォルダを作成
│   ├── action.ts // action creatorを作成
│   ├── reducer.ts // reducerを作成
│   └── types.ts // 型定義を作成
└── index.ts // storeを作成

actionTypes.tsを作成

・actionを識別するtypeを定義する。
・as constを付けて

store/actionTypes.ts
export const ActionTypes = {
    increment: 'INCREMENT',
    decrement: 'DECREMENT',
} as const

型定義を作成

・stateの型を作成
・actionの型を作成

store/counter/types.ts
import { Action } from "redux";
import { ActionTypes } from "../actionTypes";

export type Count = {
    value : number
}

interface incrementAction extends Action {
    type: typeof ActionTypes.increment,
}

interface decrementAction extends Action {
    type: typeof ActionTypes.decrement,
}

export type CountActionTypes = incrementAction | decrementAction

action creatorを作成

・実際にactionを返す関数を作成

store/counter/action.ts
import { ActionTypes } from "../actionTypes";
import { CountActionTypes } from "./types";

export const increment = () : CountActionTypes => {
    return {
        type: ActionTypes.increment
    }
}

export const decrement = () : CountActionTypes => {
    return {
        type: ActionTypes.decrement
    }
}

reducerを作成

・stateの初期値を作成
・reducerの処理を作成

store/counter/reducer.ts
import { ActionTypes } from "../actionTypes";
import { Count, CountActionTypes } from "./types";

const initialState : Count = {
    value: 0,
}

export const CountReducer = (state = initialState, action: CountActionTypes) : Count => {
    switch (action.type) {
        case ActionTypes.increment:
            return { ...state, value: state.value + 1 }
        case ActionTypes.decrement:
            return { ...state, value: state.value - 1 }
    }
    return state;
}

storeを作成

store/index.ts
import { combineReducers, createStore } from "redux";
import { CountReducer } from "./counter/reducer";

const RootReducer = combineReducers({
    count: CountReducer,
})

export type RootState = ReturnType<typeof RootReducer>

const store = createStore(RootReducer)
export default store

providerをセット

index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import './index.css';
import reportWebVitals from './reportWebVitals';
import store from './store';

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

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

stateの値を参照する

・useSelecorを使ってcount用のstateを取得

App.tsx
import React from 'react';
import { useSelector } from 'react-redux';
import './App.css';
import { RootState } from './store';

function App() {
  const countState = useSelector((state: RootState) => state.count)
  return (
    <div>
      { countState.value }
    </div>
  );
}

export default App;

stateを更新する

・dispatchを使って更新する

App.tsx
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import './App.css';
import { RootState } from './store';
import { decrement, increment } from './store/counter/action';

function App() {
  const countState = useSelector((state: RootState) => state.count)
  const dispatch = useDispatch()

  const OnIncrement = () => {
    dispatch(increment())
  }

  const OnDecrement = () => {
    dispatch(decrement())
  }

  return (
    <div>
      { countState.value }
      <button onClick={OnIncrement}>+1</button>
      <button onClick={OnDecrement}>-1</button>
    </div>
  );
}

export default App;