React

連載[最短で理解する] ReduxToolKit(2)(React x TypeScript) example minimum counter with createActionThunk

連載[最短で理解する] ReduxToolKit(2)(React x TypeScript) example minimum counter with createActionThunk

[最短で理解する] ReduxToolKit(React x TypeScript) example minimum counter
[最短で理解する] ReduxToolKit(React x TypeScript) example minimum counter

今回は前回からのコードにextraReducerをつけたもの。非同期部分を追加しました

※コードで動いている様子はページしたのリンクに貼り付けておきます

前回の様子はこちら(基本的なRTKのセッティングと各アクションまで)

チュートリアル

フォルダ構成は変わらず

  • 修正箇所はslice部分とpageで呼ぶところ
  • 型引数を渡すところ

src/features/counterPage.tsx

import * as React from "react"
import * as ReactRedux from "react-redux"
import * as CounterModule from "./counterSlice"
import { RootState } from "../rootReducer"
import Button from "../components/Button"

export function Component(){
  const dispatch = ReactRedux.useDispatch()
  const count = ReactRedux.useSelector<RootState, number>((state) => state.counter.count)

  const onClickDown = () => {
    dispatch(CounterModule.countDown(-1)) // いったん
  }

  const onClickUp = () => {
    dispatch(CounterModule.countUp(1)) //いったん
  }

    React.useEffect(() => {
    dispatch(CounterModule.getData()); // new 呼ぶ
  }, [dispatch]);

  return (
    <React.Fragment>
      <div>{count}</div>
      <Button onClick={() => onClickUp()} text="up" />
      <Button onClick={() => onClickDown()} text="down" />
    </React.Fragment>
  )
}

/src/components/Button.tsx


import * as React from "react"

type Props = {
  text: string
  onClick: () => void
}
function Button(props : Props) {
  return (
    <button onClick={props.onClick}>{props.text}</button>
  )
}

export default Button

src/features/counterSlice.ts

import { createSlice, PayloadAction, createAsyncThunk } from
"@reduxjs/toolkit";
import { Store, ActionDispatch } form "../store"

type State = {
  count: number;
  loading: boolean;
  error: boolean;
  errorMessage: string;
};

const initialState: State = {
  count: 1,
  loading: false,
  error: false,
  errorMessage: ""
};

// 追加 // response
type Responce = {
  value: number;
};

// 追加 actionCreaterが呼ばれる時の引数
type GetDataArgument = {
  value: number;
};

// 追加
type ActionDispatch = {
  dispatch: AppDispatch;
  state: Store;
};

export const getData = createAsyncThunk<Responce,GetDataArgument, ActionDispatch>("counnter/getData", async (action) => { // それぞれ型引数を渡す
  const res = await fetch(
    "https://jsondata.okiba.me/v1/json/6qk09201017012231" // API
  ).then((value) => value.json());
  console.log(res);
  return res;
});

const counter = createSlice({
  name: "counter",
  initialState,
  reducers: {
    countUp: (state: State, action: PayloadAction<number>) => {
      state.count += action.payload;
    },
    countDown: (state: State, action: PayloadAction<number>) => {
      state.count += action.payload;
    }
  },
  extraReducers: (builder) => { // builder
    builder
      .addCase(getData.pending, (state, action) => { // ここにケースを追加していく
        return { ...state, loading: true, error: false, errorMessage: "" };
      })
      .addCase(getData.fulfilled, (state, action) => {
        return {
          ...state,
          count: state.count + action.payload.value,
          loading: false,
          error: false,
          errorMessage: ""
        };
      })
      .addCase(getData.rejected, (state) => {
        return {
          ...state,
          loading: false,
          error: true,
          errorMessage: "error"
        };
      });
  }
});

export const { countUp, countDown } = counter.actions;

export default counter.reducer;

src/rootReducer.ts

import { combineReducers } from "@reduxjs/toolkit"
import counterModules from "./features/counterSlice"

const rootReducer = combineReducers({
  counter: counterModules
})

export type RootState = ReturnType<typeof rootReducer>

export default rootReducer

src/store.ts

import { configureStore } from '@reduxjs/toolkit'

import rootReducer from "./rootReducer"

const store = configureStore({
  reducer: rootReducer
})

export type AppDispatch = typeof store.dispatch

export default store

src/App.ts

import React from 'react';
import * as Counter from "./features/CounterPage"
import './App.css';

function Component() {
  return (
    <div className="App">
      <Counter.Component />
    </div>
  );
}

export default Component;

src/index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as App from './App';
import { Provider } from "react-redux"
import store from "./store"

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

今回のコード

全然関係ないですが、もはやこれが理解できるようになると「Reduxできます」は売りじゃなくなってきますね...

今度はミドルウェアを追加します-


「武骨日記の」プライバシーポリシーに関して
プライバシーポリシー
個人情報取り扱いに関して

サイトTOP
私は何者か
29歳よしもと芸人がWebデザイナー未経験で学校に通い5年後フリーランスのフロントエンドエンジニアになるためにやった9つのこと
フロント記事
フロントエンドエンジニア
フロントエンド記事(タグ)
TypeScript
TypeScript練習問題集
【TypeScript】TypeSript中級者になる為に知っておくと良い108個のこと
JavaScript練習問題
styled-componentsの使い方
SCSS問題集
GraphQL「Apollo x Relay-Style-Cursor-Pagination(リレースタイルカーソルページネーション)」
recomposeと仲良くなりたい
機動戦士FlowType
初めてReactNativeWebを触ってみて
Ramda.jsシグネチャの読み方
環境変数の話
いちごタルトの作り方
フロントエンドエンジニア豚汁の作り方


「武骨日記の」プライバシーポリシーに関して
プライバシーポリシー

株式会社TerraceTechについて
最近起業しました。
株式会社TerraceTech

SNS
しずかなインターネット kenjimorita
インド旅 instagram
適当な日常写真 instagram
シュールさーん instagram
シュールさーん LINEスタンプ
もりたけんじTwitter
ネタ帳Twitter
note

FaceBook -kennji.morita-

わたしが結成しているWebチームではWeb未経験者、フリーランスの方へのお仕事を紹介しています。
また個人レッスンしてほしい生徒も募集中です。
もしご興味ある方はチャットからご連絡ください。

※業務連絡やお久しぶり連絡もチャットからお願いします。