😀
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;
Discussion