Skip to content

React プロジェクトセットアップ

React および TypeScript を使った Web プロジェクトの初期セットアップ方法について記載します。

プロジェクト作成

プロジェクトの新規作成方法について記載します。

redux

  • プロジェクトを新規作成します。
npx create-react-app easyweb --template typescript
  • redux および他必要なものをインストールします。
cd easyweb
npm install redux react-redux redux-thunk react-router-dom axios

scss

  • XXX.module.scssを使えるようにするため、下記をインストールします。
npm install sass node-sass

Redux 向け実装

Redux を動かすために必要な改修およびサンプルコードについて記載します。サンプルでは + ボタンと - ボタンによってカウンターを増減させる画面を実装します。

ディレクトリ構成

自動生成されるもののうち、手を入れる必要がないものについては除外しています。

.
└── src
    ├── app
       ├── rootReducer.ts         # reducerの統合
       └── store.ts               # store作成
    ├── app.module.scss            # Appの装飾
    ├── App.tsx                    # アプリケーションコンテンツのroot
    ├── features
       └── counter
           ├── counterAction.ts   # 各コンポーネントの動作
           ├── counterReducer.ts  # 各コンポーネントの状態変化関数の実装
           ├── counterState.ts    # 各コンポーネントの状態定義
           └── Counter.tsx        # 各コンポーネントの本体定義
    ├── index.module.scss          # ページ全体の装飾
    ├── index.tsx                  # アプリケーションのエントリ
    └── .prettierrc                # コードフォーマットの定義

クラス一覧

各クラスに必要な修正、または新規作成方法について記載します。

.prettierrc

コードのフォーマットに関する設定を定義します。

{
  "tabWidth": 2,
  "useTabs": false,
  "trailingComma": "none",
  "semi": true
}

app/store.ts

各種 reducer を取りまとめた store を作成します。

import { legacy_createStore as createStore } from "redux";
import { rootReducer } from "./rootReducer";

export const store = createStore(rootReducer);

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

app/rootReducer.ts

reducer を統合します。reducer が増えるたびにrootReducerへの追記が必要です。

import { combineReducers } from "redux";
import { counterReducer } from "../features/counter/counterReducer";

/**
 * reducerを統合します。
 */
export const rootReducer = combineReducers({
  counter: counterReducer,
});

index.tsx

ProviderコンポーネントでAppコンポーネントをラップします。

<Provider store={store}>
  <App />
</Provider>

App.tsx

ルーティングを設定します。

import style from "./app.module.scss";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Counter from "./features/counter/Counter";

function App() {
  return (
    <BrowserRouter>
      <div className={style.body}>
        <Routes>
          <Route path="/" element={<Counter />} />
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;

features/counter/Counter.tsx

画面コンテンツおよび action の呼び出しを定義します。

import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { AppDispatch, RootState } from "../../app/store";
import { counterActions } from "./counterAction";

interface Props {}

/**
 * カウンターを表示するコンポーネントです。
 *
 * @param props
 * @returns
 */
const Counter: React.FC<Props> = (props) => {
  const count = useSelector((state: RootState) => state.counter.value);
  const dispatch = useDispatch<AppDispatch>();

  /**
   * マイナスボタン押下時の動作を定義します。
   */
  const handleOnClickDecrementButton = () => {
    dispatch(counterActions.decrement());
  };

  /**
   * プラスボタン押下時の動作を定義します。
   */
  const handleOnClickIncrementButton = () => {
    dispatch(counterActions.increment());
  };

  return (
    <div>
      <h2>Counter: {count}</h2>
      <button onClick={handleOnClickDecrementButton}>-</button>
      <button onClick={handleOnClickIncrementButton}>+</button>
    </div>
  );
};

export default Counter;

features/counter/counterState.ts

画面の状態を管理するための構造体を宣言します。

/**
 * カウンターの状態を保持するstateです。
 */
export interface CounterState {
  value: number;
}

/**
 * CounterStateの初期状態です。
 */
export const initialCounterState: CounterState = {
  value: 0,
};

features/counter/counterAction.ts

各コンポーネントのアクションを定義します。

/**
 * Counterのaction typeを定義します。
 */
export const CounterActionTypeConst = {
  INCREMENT: "INCREMENT",
  DECREMENT: "DECREMENT",
} as const;

/**
 * Counterのactionを定義します。
 */
export const counterActions = {
  /**
   * 値を増加させます。
   */
  increment: () => ({
    type: CounterActionTypeConst.INCREMENT,
  }),

  /**
   * 値を減少させます。
   */
  decrement: () => ({
    type: CounterActionTypeConst.DECREMENT,
  }),
};

export type CounterActionTypes = ReturnType<
  (typeof counterActions)[keyof typeof counterActions]
>;

features/counter/counterReducer.ts

各コンポーネントの状態変化を返す関数を定義します。

import { CounterActionTypeConst, CounterActionTypes } from "./counterAction";
import { CounterState, initialCounterState } from "./counterState";

/**
 * Counterのstateを更新します。
 *
 * @param state CounterState
 * @param action counterAction
 * @returns CounterState
 */
export const counterReducer = (
  state = initialCounterState,
  action: CounterActionTypes
): CounterState => {
  switch (action.type) {
    case CounterActionTypeConst.INCREMENT:
      return {
        ...state,
        value: state.value + 1,
      };
    case CounterActionTypeConst.DECREMENT:
      return {
        ...state,
        value: state.value - 1,
      };
    default:
      return state;
  }
};

index.module.scss

Web ページ全体の装飾を定義します。

body {
  background-color: #050027;
  color: #dddddd;
}

app.module.scss

App 配下の装飾を定義します。

.body {
  padding: 60px 60px 60px 60px;
}

起動

npm start

起動後、http://localhost:3000 で画面が確認できます。