Rental Application (React & Spring boot Microservice) - 25 : redux-saga

10880 ワード

#1 Redux-sagaアプリケーション


redux-sagaは、これらの動作を監視し、特定の動作が発生した場合に処理する.まずredux-sagaとaxiosをインストールします.axiosは、APIをバインドするためのライブラリです.
npm install redux-saga axios
  • ./src/lib/api/client.js
  • import axios from 'axios';
    
    const client = axios.create();
    
    export default client;
    CORSエラーを回避するために、エージェントを8900番ポート(apigate-service)に設定します.
  • pacakage.json
  • {
      ...
      "proxy": "http://localhost:8900/"
    }
    管理メンバーに必要なapiは、次のように作成されます.既存のauth-serviceのエンドポイントurlを使用します.
  • ./src/lib/api/auth.js
  • import client from './client';
    
    export const login = ({
        email,
        password
    }) => client.post('/auth-service/login', {
        email,
        password
    });
    
    export const register = ({
        email,
        password,
        nickname,
        phoneNumber
    }) => client.post('/auth-service/register', {
        email,
        password,
        nickname,
        phoneNumber
    });
    
    export const getUser = ({ userId }) => client.getUser('/auth-service/:userId/getUser', { userId });
    次に、動作関数の開始時間と終了時間を示すload reducsモジュールを作成します.
  • ./src/modules/loading.js
  • import { createAction, handleActions } from "redux-actions";
    
    const START_LOADING = 'loading/START_LOADING';
    const FINISH_LOADING = 'loading/FINISH_LOADING';
    
    export const startLoading = createAction(
        START_LOADING,
        requestType => requestType,
    );
    
    export const finishLoading = createAction(
        FINISH_LOADING,
        requestType => requestType,
    );
    
    const initialState = {};
    
    const loading = handleActions(
        {
            [START_LOADING]: (state, action) => ({
                ...state,
                [action.payload]: true,
            }),
            [FINISH_LOADING]: (state, action) => ({
                ...state,
                [action.payload]: false,
            }),
        },
        initialState,
    );
    
    export default loading;
  • ./src/modules/index.js
  • import { combineReducers } from "redux";
    import auth from './auth';
    import loading from './loading';
    
    const rootReducer = combineReducers(
        {
            loading,
            auth,
        },
    );
    
    export default rootReducer;
    createRequestSagaは、動作関数が正常に動作しているときにAPI内のデータの要求と応答を処理するユーティリティ関数です.
  • ./lib/createRequestSaga.js
  • import { call, put } from "@redux-saga/effects";
    import { startLoading, finishLoading } from "../modules/loading";
    
    export const createRequestActionTypes = type => {
        const SUCCESS = `${type}_SUCCESS`;
        const FAILURE = `${type}_FAILURE`;
    
        return [type, SUCCESS, FAILURE];
    };
    
    export default function createRequestSaga(type, request) {
        const SUCCESS = `${type}_SUCCESS`;
        const FAILURE = `${type}_FAILURE`;
    
        return function*(action) {
            yield(put(startLoading(type)));
    
            try {
                const response = yield call(request, action.payload);
    
                yield put({
                    type: SUCCESS,
                    payload: response.data,
                });
            } catch(e) {
                yield put({
                    type: FAILURE,
                    payload: e,
                    error: true,
                });
            }
    
            yield put(finishLoading(type));
        };
    }
    1)createRequestActionType:アクションタイプのコード.たとえば、ログインしたアクションタイプを「auth/LOgin」、「auth/LOgin SUCCESS」、「auth/LOgin FAILURE」と呼ぶ場合、この方法を使用してアクションタイプに変数を付与します.
    2)createRequestSaga:Sagaリクエストコード.たとえば、auth/LOginという名前のタイプ、アイデンティティ、パスワードです./src/lib/api/auth.jsに存在するloginリクエストを受信すると、ジェネレータ関数を使用してリクエストが処理されます.
    では、これらのコードを利用してapi処理のコードを完成します.
  • ./src/modules/auth.js
  • import { createAction, handleActions } from "redux-actions";
    import produce from 'immer';
    import { takeLatest } from "@redux-saga/core/effects";
    import createRequestSaga,{ 
        createRequestActionTypes 
    } from "../lib/createRequestSaga";
    import * as authAPI from '../lib/api/auth';
    
    const CHANGE_FIELD = 'auth/CHANGE_FIELD';
    const INITIALIZE_FORM = 'auth/INITIALIZE_FORM';
    
    const [LOGIN, LOGIN_SUCCESS, LOGIN_FAILURE] = createRequestActionTypes('auth/LOGIN');
    const [REGISTER, REGISTER_SUCCESS, REGISTER_FAILURE] = createRequestActionTypes('auth/REGISTER');
    
    export const changeField = createAction(
        CHANGE_FIELD, ({
            form,
            key,
            value
        })  => ({
            form,
            key,
            value,
        }),
    );
    
    export const initializeForm = createAction(INITIALIZE_FORM, form => form);
    
    export const login = createAction(LOGIN, ({ 
        email, 
        password 
    }) => ({
        email,
        password
    }));
    
    export const register = createAction(REGISTER, ({
        email,
        password,
        nickname,
        phoneNumber
    }) => ({
        email,
        password,
        nickname,
        phoneNumber
    }));
    
    const loginSaga = createRequestSaga(LOGIN, authAPI.login);
    const registerSaga = createRequestSaga(REGISTER, authAPI.register);
    
    export function* authSaga() {
        yield takeLatest(LOGIN, loginSaga);
        yield takeLatest(REGISTER, registerSaga);
    }
    
    const initialState = {
        register: {
            email: '',
            password: '',
            passwordConfirm: '',
            nickname: '',
            phoneNumber: '',
        },
        login: {
            email: '',
            password: '',
        },
        auth: null,
        authError: null,
    };
    
    const auth = handleActions(
        {
            [CHANGE_FIELD]: (state, { payload: { form, key, value }}) => 
            produce(state, draft => {
                draft[form][key] = value;
            }),
            [INITIALIZE_FORM]: (state, { payload: form }) => ({
                ...state,
                [form]: initialState[form],
                authError: null,
            }),
            [LOGIN_SUCCESS]: (state, { payload: auth }) => ({
                ...state,
                authError: null,
                auth
            }),
            [LOGIN_FAILURE]: (state, { payload: error }) => ({
                ...state,
                authError: error,
            }),
            [REGISTER_SUCCESS]: (state, { payload: auth }) => ({
                ...state,
                authError: null,
                auth,
            }),
            [REGISTER_FAILURE]: (state, { payload: error }) => ({
                ...state,
                authError: error,
            }),
        },
        initialState,
    );
    
    export default auth;
  • ./src/modules/index.js
  • import { combineReducers } from "redux";
    import { all } from 'redux-saga/effects';
    import auth, { authSaga } from './auth';
    import loading from './loading';
    
    const rootReducer = combineReducers(
        {
            loading,
            auth,
        },
    );
    
    export function* rootSaga() {
        yield all([authSaga()]);
    }
    
    export default rootReducer;
    ./src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    import { Provider } from 'react-redux';
    import { createStore, applyMiddleware } from 'redux';
    import { composeWithDevTools } from 'redux-devtools-extension';
    import createSagaMiddleware from '@redux-saga/core';
    import rootReducer, { rootSaga } from './modules';
    import { BrowserRouter } from 'react-router-dom';
    
    const sagaMiddleware = createSagaMiddleware();
    const store = createStore(
      rootReducer,
      composeWithDevTools(applyMiddleware(sagaMiddleware))
    );
    
    sagaMiddleware.run(rootSaga);
    
    ReactDOM.render(
      <Provider store={ store }>
        <BrowserRouter>
          <App />
        </BrowserRouter>
      </Provider>,
      document.getElementById('root')
    );
    reportWebVitals();
    道徳に関連するコードを作成したので、Formコンポーネントの登録とログインを継続できるようにコードを作成します.

    2会員加入

  • ./src/components/auth/RegisterForm.js
  • import React, { useEffect } from 'react';
    import { useDispatch, useSelector } from 'react-redux';
    import { changeField, initializeForm, register } from '../../modules/auth';
    import AuthForm from './AuthForm';
    import { withRouter } from 'react-router-dom';
    
    const RegisterForm = ({ history }) => {
        const dispatch = useDispatch();
        const { 
            form, 
            auth, 
            authError
        } = useSelector(({ 
            auth,
        }) => ({
            form: auth.register,
            auth: auth.auth,
            authError: auth.authError,
        })); 
    
        ...
    
        // Handler that registers form
        const onSubmit = e => {
            e.preventDefault();
    
            const { 
                email, 
                password,
                passwordConfirm,
                nickname,
                phoneNumber, 
            } = form;
    
            if(password !== passwordConfirm) {
                // setError
                
                return;
            }
    
            if([
                email, 
                password,
                passwordConfirm,
                nickname,
                phoneNumber,
            ].includes('')) {
                // setError
                return;
            }
    
            dispatch(register({
                email,
                password,
                nickname,
                phoneNumber,
            }));
    
            history.push('/');
        };
    
        ...
    };
    
    export default withRouter(RegisterForm);
    discovery-service、apiGateway-service、config-server、auth-serviceを起動し、テスト結果を表示します.



    会員が加入している姿がよく見えます.
    次の記事では、ログインに関連するredoxを作成する方法について説明します.

    リファレンス

  • 反応技術-著者:金民俊