Masayan tech blog.

  1. ブログ記事一覧>
  2. Laravel8・React・ReduxでSPA開発をしてみる (ストア構築編)

Laravel8・React・ReduxでSPA開発をしてみる (ストア構築編)

公開日

環境

  • windows10
  • DockerDesktop for Win 3.5.x
  • Laravel 8.x
  • PHP 8.x
  • node v14.17.4
  • npm 6.14.14
  • React 17.0.1
  • VsCode
  • gitbash 2.32.0.1

事前準備

laravelにreactをインストールするとデフォルトだとapp.jsが起点になっているため、少し開発しづらいので、起点をindex.jsに変更する作業を行います。

コンパイル対象(元)のファイルを app.js -> index.jsに変更する

webpack.mix.js

mix.js("resources/js/index.js", "public/js")
.react()
// .sass('resources/sass/app.scss', 'public/css');
.postCss("resources/css/app.css", "public/css", [require("tailwindcss")]);

App.jsxを作成

import React from "react";

const App = () => {
return <p>App</p>;
};

export default App;

index.jsを作成

app.jsをコピーしてindex.jsを作成し、App.jsxを読み込ませます

require("./bootstrap");

import React from "react";
import ReactDOM from "react-dom";
import { Switch, Route, useHistory, BrowserRouter } from "react-router-dom";
import "../css/app.css";
import App from "./App";
import CalendarFrame from "./components/calendar/CalendarFrame";

if (document.getElementById("root")) {
  ReactDOM.render(
    <BrowserRouter>
      <App />
      <Switch>
        <Route path="/calendar(/:date_id)?" component={CalendarFrame} />
      </Switch>
    </BrowserRouter>,

    document.getElementById("root")
  );
}

app.blade.phpを修正

ルートのidをappからrootに変更し、コンパイルされたjsファイルの読み込み先を js/app.jsからjs/index.jsに変更する

<script src="{{ asset('js/index.js') }}" defer></script>

〜省略〜
<body>
  <div id="root">

  </div>
</body>

ストアを構築する

redux関連のライブラリをinstall

npm i react-redux redux-thunk redux-devtools-extension

ディレクトリ準備

ストアの作成

/resources/js/reducks/store/initialState.js

export const initialState = {
  users: {
    email: "",
    isSignedIn: false,
    uid: "",
    username: ""
  }
};

/resources/js/reducks/store/store.js

import {
  createStore as reduxCreateStore,
  combineReducers,
} from "redux";
import thunk from "redux-thunk";
import { UsersReducer } from "../users/reducers";

export default function createStore(history) {
  return reduxCreateStore(
    combineReducers({
      users: UsersReducer,
    }),
  );
}

actionsの作成

 actionsはデータをreducersに渡すだけの役割

以下を作成(サインイン・アウト用のactionsを作成)

/resources/js/reducks/users/actions.js

export const SIGN_IN = "SIGN_IN";
export const signInAction = (userState) => {
  return {
    type: "SIGN_IN",
    payload: userState
  }
};

export const SIGN_OUT = "SIGN_OUT";
export const signOutAction = () => {
  return {
    type: "SIGN_OUT",
    payload: null
  }
};

reducersの作成

actionsから渡ってきたデータをもとにストアのstateを変更する役割

以下を作成(サインイン・アウト用のreducersを作成)

/resources/js/reducks/users/reducers.js

import * as Actions from "./actions";
import { initialState } from "../store/initialState";

export const UsersReducer = (state = initialState.users, action) => {
  switch (action.type) {
    case Actions.SIGN_IN:
    return {
      ...state,
      ...action.payload,
    };
    case Actions.SIGN_OUT:
    return {
      ...initialState.users,
    };
    default:
      return state;
  }
};

storeとreactアプリの接続

/resources/js/index.js

・ProviderとcreateStoreをimport

・読み込んだcreateStoreを実行

export const store = createStore();

・ProviederコンポーネントでReactアプリをラッピングしつつ、定義したstoreをpropsで渡す

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

最終的なコード

require("./bootstrap");

import React from "react";
import ReactDOM from "react-dom";
import createStore from "./reducks/store/store";
import { Provider } from "react-redux";
import { Switch, Route, useHistory, BrowserRouter } from "react-router-dom";
import "../css/app.css";
import App from "./App";
import CalendarFrame from "./components/calendar/CalendarFrame";

export const store = createStore();

if (document.getElementById("root")) {
  ReactDOM.render(
    <Provider store={store}>
      <BrowserRouter>
        <App />
        <Switch>
          <Route path="/calendar(/:date_id)?" component={CalendarFrame} />
        </Switch>
      </BrowserRouter>
    </Provider>,
    document.getElementById("root")
  );
}

ボタンのクリックイベントに先ほど設定したsignInActionを実行するリスナーを設定する

import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { signInAction } from "./reducks/users/actions";

const App = () => {
  const dispatch = useDispatch();
  const selector = useSelector((state) => state);
  console.log(selector.users);

  return (
    <>
     <button
       className="border"
       onClick={() =>
         dispatch(
           signInAction({
             uid: "0001",
             username: "テストマン",
             isSignedIn: true,
           })
         )
       }
     >サインイン
     </button>
    </>
  );
};

export default App;

ボタンをクリックして、コンソールにstoreのusersの情報が出力され、サインイン後の状態になっていればOK(isSinedinがtrue、uidとusernameに値が入る)

まとめ

いかがでしたでしょうか。本記事では、Laravel8・ReactのSPAに、ストア構築用のライブラリReduxを導入して認証の仕組みを構築する方法について紹介しています。