Rental Application (React & Spring boot Microservice) - 24 : Redux

55197 ワード

#1 LoginPage


redixを適用する前に、LoginPageとRegisterPageを作成します.
共同で使用するbuttonとinputを作成します.
  • /src/components/common/Input.js
  • import React from 'react';
    import styled from 'styled-components';
    import palette from '../../lib/styles/palettes';
    
    const StyledInput = styled.input`
        width: 100%;
        height: 40px;
        border: 2px solid #aaa;
        border-radius: 4px;
        margin: 8px 0;
        outline: none;
        padding: 8px;
        box-sizing: border-box;
        transition: .3s;
        &:focus {
            border-color: ${ palette.blue[2] };
            box-shadow: 0 0 8px 0 ${ palette.blue[2] };
        }
    `;
    
    const Input = props => <StyledInput { ...props } />
    
    export default Input;
    const Input = props => <StyledInput { ...props } />において、propsは、name、onChange、valueなどの要素を呼び出すときに受信されるパラメータであり、これは、これらのパラメータをStyledInputに同じように入れることを意味する.
  • ./src/components/common/BorderButton.js
  • import React from 'react';
    import styled from 'styled-components';
    import palette from '../../lib/styles/palettes';
    
    const StyledButton = styled.button`
        width: 100%;
        height: 50px;
        border-radius: 4px;
        border: 2px solid ${palette.blue[1]};
        margin: 8px 0;
        padding: 8px;
        box-sizing: border-box;
        transition: .3s;
        background-color: #ffffff;
        color: ${palette.blue[1]};
        font-size: 20px;
    `;
    
    const BorderButton = props => <StyledButton { ...props } />
    
    export default BorderButton;
  • ./src/components/common/FullButton.js
  • import React from 'react';
    import styled, { css } from 'styled-components';
    import palette from '../../lib/styles/palettes';
    
    const StyledButton = styled.button`
        width: 100%;
        height: 50px;
        border-radius: 4px;
        border: none;
        margin: 8px 0;
        outline: none;
        padding: 8px;
        box-sizing: border-box;
        transition: .3s;
        background-color: ${palette.blue[1]};
        color: #ffffff;
        font-size: 20px;
        &:hover {
            width: 100%;
            height: 50px;
            border-radius: 4px;
            border: none;
            margin: 8px 0;
            outline: none;
            padding: 8px;
            box-sizing: border-box;
            transition: .3s;
            background-color: ${palette.blue[2]};
            color: #ffffff;
            font-size: 20px;
        }
        ${
            props =>
            props.red &&
            css`
                background: ${ palette.red[0] };
                &: hover {
                    background: ${ palette.red[1] };
                }
            `  
        }
    `;
    
    const FullButton = props => <StyledButton { ...props } />
    
    export default FullButton;
    共通に使用する構成部品が作成されている以上、次のディレクトリを作成してLoginPageを作成します.
  • ./src/components/auth/AuthTemplate.js
  • import React from 'react';
    import styled from 'styled-components';
    import palette from '../../lib/styles/palettes';
    
    const AuthTemplateBlock = styled.div`
        left: 0;
        top: 0;
        bottom: 0;
        right: 0;
        background: ${ palette.gray[2] };
        padding-top: 130px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
    `;
    
    const WhiteBox = styled.div`
        box-shadow: 0 0 8px rgba(0, 0, 0, 0.025);
        padding: 2rem;
        width: 80%;
        background: white;
        border-radius: 2px;
    `;
    
    
    const AuthTemplate = ({ children }) => {
        return(
            <AuthTemplateBlock>
                <WhiteBox>
                    { children }
                </WhiteBox>
            </AuthTemplateBlock>
        );
    };
    
    export default AuthTemplate;
    AutheTemplateBlockはAutheForm素子を保護するための素子である.
  • ./src/components/AuthForm.js
  • import React from 'react';
    import styled from 'styled-components';
    import { Link } from 'react-router-dom';
    import palette from '../../lib/styles/palettes';
    import FullButton from '../common/FullButton';
    import BorderButton from '../common/BorderButton';
    import Input from '../common/Input'
    
    const AuthFormBlock = styled.div`
        h3 {
            margin: 0;
            color: ${ palette.gray[8] };
            margin-bottom: 1rem;
        }
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
    `;
    
    const Header = styled.div`
        margin-top: 20px;
        margin-bottom: 30px;
        width: 60%;
        font-size: 25px;
        padding-left: 10%;
        color: ${palette.blue[2]};
        font-weight: bold;
    `;
    
    const ErrorMessage = styled.div`
        color: red;
        text-align: center;
        font-size: 14px;
        margin-top: 1rem;
    `;
    
    const textMap = {
        login: 'login',
        register: 'register'
    };
    
    const AuthForm = ({ 
        type, 
        form, 
        onSubmit, 
        error 
    }) => {
        const text = textMap[type];
    
        return(
            <AuthFormBlock>
                { text === 'login' ? (
                    <>
                        <Header>
                            물건 대여 플랫폼에 <br />
                            오신 것을 환영합니다!
                        </Header>
                        <form onSubmit={ onSubmit }>
                            <Input 
                                autoComplete="email"
                                name="email"
                                placeholder="E-mail"
                                value={ form.email }
                            />
                            <Input 
                                autoComplete="new-password"
                                name="password"
                                placeholder="Password"
                                type="password"
                                value={ form.password }
                            />
                            { error && <ErrorMessage>{ error }</ErrorMessage>}
                            <FullButton>
                                Login        
                            </FullButton>
                            <BorderButton>
                                <Link to="/auth/RegisterPage">
                                    Sign Up
                                </Link>
                            </BorderButton>
                        </form>
                    </>
                ) : (
                    <>
                        <Header>
                            물건 대여 플랫폼에 <br />
                            처음 오셨군요!
                        </Header>
                        <form onSubmit={ onSubmit }>
                            <Input 
                                autoComplete="email"
                                name="email"
                                placeholder="이메일을 입력해주세요"
                                value={ form.email }
                            />
                            <Input 
                                autoComplete="new-password"
                                name="password"
                                placeholder="비밀번호를 입력해주세요"
                                type="password"
                                value={ form.password }
                            />
                            <Input 
                                autoComplete="new-password"
                                name="passwordConfirm"
                                placeholder="비밀번호를 재입력해주세요"
                                type="password"
                                value={ form.passwordConfirm }
                            />
                            <Input 
                                autoComplete="nickname"
                                name="nickname"
                                placeholder="닉네임을 입력해주세요"
                                value={ form.nickname }
                            />
                            <Input 
                                autoComplete="phoneNumber"
                                name="phoneNumber"
                                placeholder="'-'를 제외하고 핸드폰 번호를 입력해주세요"
                                value={ form.phoneNumber }
                            />
                            { error && <ErrorMessage>{ error }</ErrorMessage>}
                            <FullButton>
                                Sign Up
                            </FullButton>
                        </form>
                    </>
                )}
            </AuthFormBlock>
        );
    };
    
    export default AuthForm;
    ログインとコストでよく使用されるフォーム.ここでは三重演算子を用い,typeをパラメータとしてloginとregisterの間で対応するフォームをそれぞれ呼び出す.
  • ./src/components/auth/LoginForm.js
  • import React, { useState } from 'react';
    import { withRouter } from 'react-router-dom';
    import AuthForm from './AuthForm';
    
    const LoginForm = ({ history }) => {
        const [error, setError] = useState(null);
        
    
        const onSubmit = e => {
            // Login Button EventListener
            
            e.preventDefault();
        };
    
        return (
            <AuthForm
                type='login'
                form={ form }
                onSubmit={ onSubmit }
                error={ error }
            />
        );
    };
    
    export default withRouter(LoginForm);
  • ./src/pages/LoginPage.js
  • import React from 'react';
    import AuthTemplate from '../components/auth/AuthTemplate';
    import LoginForm from '../components/auth/LoginForm';
    import HeaderTemplate from '../components/common/HeaderTemplate';
    
    const LoginPage = () => {
        return(
            <>
                <HeaderTemplate />
                <AuthTemplate>
                    <LoginForm />
                </AuthTemplate>
            </>
        );
    };
    
    export default LoginPage;
    ログインページが作成され、アプリケーションがルーティング機能を有効にします.jsに入れて
  • ./App.js
  • import React from 'react';
    import { Route } from 'react-router-dom';
    import HomePage from './pages/HomePage';
    import LoginPage from './pages/LoginPage';
    import MyPage from './pages/MyPage';
    import RegisterPage from './pages/RegisterPage';
    
    const App = () => {
        return(
            <>
                ...
                <Route 
                    component={ LoginPage }
                    path="/auth/LoginPage"
                    exact
                />
            </>
        );
    };
    
    export default App;
    http://localhost:3000/auth/LoginPage道。に入ってログインページを確認します

    よく撮れている様子が見られますそれでは、これを利用して会員登録ページを作成しましょう

    #2 RegisterPage

  • ./src/components/auth/RegisterForm.js
  • import React from 'react';
    import AuthForm from './AuthForm';
    import { withRouter } from 'react-router-dom';
    
    const RegisterForm = ({ history }) => {
        // Handler that registers form
        const onSubmit = e => {
            e.preventDefault();
    
        };
    
        return (
            <AuthForm
                type='register'
                form={ form }
                onSubmit={ onSubmit }
            />
        );
    };
    
    export default withRouter(RegisterForm);
  • ./src/pages/RegisterPage.js
  • import React from 'react';
    import AuthTemplate from '../components/auth/AuthTemplate';
    import RegisterForm from '../components/auth/RegisterForm';
    import HeaderTemplate from '../components/common/HeaderTemplate';
    
    const RegisterPage = () => {
        return (
            <>
                <HeaderTemplate />
                <AuthTemplate>
                    <RegisterForm />
                </AuthTemplate>
            </>
        );
    };
    
    export default RegisterPage;
  • ./App.js
  • import React from 'react';
    import { Route } from 'react-router-dom';
    import HomePage from './pages/HomePage';
    import LoginPage from './pages/LoginPage';
    import MyPage from './pages/MyPage';
    import RegisterPage from './pages/RegisterPage';
    
    const App = () => {
        return(
            <>
                ...
                <Route 
                    component={ LoginPage }
                    path="/auth/LoginPage"
                    exact
                />
                <Route 
                    component={ RegisterPage }
                    path="/auth/RegisterPage"
                    exact
                />
            </>
        );
    };
    
    export default App;

    ログイン、登録ページのcssが完了した以上、redoxを使用して各ページのInputの状態を管理します.

    #3 Redux


    これはreduxに関する文章です.
    https://velog.io/@biuea/LifeSports-ApplicationReactNative-Node.js-7.-Redux
    以下のパッケージをインストールします.
    npm install immer react-redux redux redux-actions redux-devtools-extension
    changeFieldでは、initializeFormアクション関数を定義します.テキストを入力すると、これらのアクション関数の役割は、テキスト要素の状態をショップに保存し、状態を初期化することです.
  • src/modules/auth.js
  • import { createAction, handleActions } from "redux-actions";
    import produce from 'immer';
    
    const CHANGE_FIELD = 'auth/CHANGE_FIELD';
    const INITIALIZE_FORM = 'auth/INITIALIZE_FORM';
    
    export const changeField = createAction(
        CHANGE_FIELD, ({
            form,
            key,
            value
        })  => ({
            form,
            key,
            value,
        }),
    );
    
    export const initializeForm = createAction(INITIALIZE_FORM, form => form);
    
    const initialState = {
        register: {
            email: '',
            password: '',
            passwordConfirm: '',
            nickname: '',
            phoneNumber: '',
        },
        login: {
            email: '',
            password: '',
        }
    };
    
    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],
            }),
        },
        initialState,
    );
    
    export default auth;
  • src/modules/index.js
  • import { combineReducers } from "redux";
    import auth from './auth';
    
    const rootReducer = combineReducers(
        {
            auth,
        },
    );
    
    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 } from 'redux';
    import { composeWithDevTools } from 'redux-devtools-extension';
    import rootReducer from './modules';
    import { BrowserRouter } from 'react-router-dom';
    
    const store = createStore(
      rootReducer,
      composeWithDevTools()
    );
    
    ReactDOM.render(
      <Provider store={ store }>
        <BrowserRouter>
          <App />
        </BrowserRouter>
      </Provider>,
      document.getElementById('root')
    );
    reportWebVitals();
  • Reactネイティブプロジェクトでは記事と同じコードが使用されています.
  • authの動作関数をrootreducerに入れ,最終的にstoreを作成してカプセル化した.このために、前に作成したLoginFormとRegisterFormからアクション関数を呼び出します.
  • LoginForm.js
  • import React, { useState } from 'react';
    import { useDispatch, useSelector } from 'react-redux';
    import { withRouter } from 'react-router-dom';
    import { changeField } from '../../modules/auth';
    import AuthForm from './AuthForm';
    
    const LoginForm = ({ history }) => {
        const [error, setError] = useState(null);
        const dispatch = useDispatch();
        const { form } = useSelector(({ auth }) => ({
            form: auth.login,
        })); 
        // For changing input, handler
        const onChange = e => {
            const { 
                value,
                name 
            } = e.target;
    
            dispatch(
                changeField({
                    form: 'login',
                    key: name,
                    value
                })
            );
        };
    
        const onSubmit = e => {
            // Login Button EventListener
            
            e.preventDefault();
        };
    
        return (
            <AuthForm
                type='login'
                form={ form }
                onChange={ onChange }
                onSubmit={ onSubmit }
                error={ error }
            />
        );
    };
    
    export default withRouter(LoginForm);
  • RegisterForm
  • import React from 'react';
    import { useDispatch, useSelector } from 'react-redux';
    import { changeField  } from '../../modules/auth';
    import AuthForm from './AuthForm';
    import { withRouter } from 'react-router-dom';
    
    const RegisterForm = ({ history }) => {
        const dispatch = useDispatch();
        const { form } = useSelector(({ auth }) => ({
            form: auth.register,
        })); 
        // For changing input, handler
        const onChange = e => {
            const { value, name } = e.target;
    
            dispatch(
                changeField({
                    form: 'register',
                    key: name,
                    value
                })
            );
        };
    
        // Handler that registers form
        const onSubmit = e => {
            e.preventDefault();
    
            if(password !== passwordConfirm) {
                return;
            }
        };
    
        return (
            <AuthForm
                type='register'
                form={ form }
                onChange={ onChange }
                onSubmit={ onSubmit }
            />
        );
    };
    
    export default withRouter(RegisterForm);
  • AuthForm
  • import React from 'react';
    import styled from 'styled-components';
    import { Link } from 'react-router-dom';
    import palette from '../../lib/styles/palettes';
    import FullButton from '../common/FullButton';
    import BorderButton from '../common/BorderButton';
    import Input from '../common/Input'
    
    ...
    
    const AuthForm = ({ 
        type, 
        form, 
        onChange, 
        onSubmit, 
        error 
    }) => {
        const text = textMap[type];
    
        return(
            <AuthFormBlock>
                { text === 'login' ? (
                    <>
                        <Header>
                            물건 대여 플랫폼에 <br />
                            오신 것을 환영합니다!
                        </Header>
                        <form onSubmit={ onSubmit }>
                            <Input 
                                autoComplete="email"
                                name="email"
                                placeholder="E-mail"
                                onChange={ onChange }
                                value={ form.email }
                            />
                            <Input 
                                autoComplete="new-password"
                                name="password"
                                placeholder="Password"
                                type="password"
                                onChange={ onChange }
                                value={ form.password }
                            />
                            { error && <ErrorMessage>{ error }</ErrorMessage>}
                            <FullButton>
                                Login        
                            </FullButton>
                            <BorderButton>
                                <Link to="/auth/RegisterPage">
                                    Sign Up
                                </Link>
                            </BorderButton>
                        </form>
                    </>
                ) : (
                    <>
                        <Header>
                            물건 대여 플랫폼에 <br />
                            처음 오셨군요!
                        </Header>
                        <form onSubmit={ onSubmit }>
                            <Input 
                                autoComplete="email"
                                name="email"
                                placeholder="이메일을 입력해주세요"
                                onChange={ onChange }
                                value={ form.email }
                            />
                            <Input 
                                autoComplete="new-password"
                                name="password"
                                placeholder="비밀번호를 입력해주세요"
                                type="password"
                                onChange={ onChange }
                                value={ form.password }
                            />
                            <Input 
                                autoComplete="new-password"
                                name="passwordConfirm"
                                placeholder="비밀번호를 재입력해주세요"
                                type="password"
                                onChange={ onChange }
                                value={ form.passwordConfirm }
                            />
                            <Input 
                                autoComplete="nickname"
                                name="nickname"
                                placeholder="닉네임을 입력해주세요"
                                onChange={ onChange }
                                value={ form.nickname }
                            />
                            <Input 
                                autoComplete="phoneNumber"
                                name="phoneNumber"
                                placeholder="'-'를 제외하고 핸드폰 번호를 입력해주세요"
                                onChange={ onChange }
                                value={ form.phoneNumber }
                            />
                            { error && <ErrorMessage>{ error }</ErrorMessage>}
                            <FullButton>
                                Sign Up
                            </FullButton>
                        </form>
                    </>
                )}
            </AuthFormBlock>
        );
    };
    
    export default AuthForm;
    上のコードのように各InputにonChangeを入れるstateがstoreで良好に保存されるように、次のクロムツールをインストールします.
    https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?page=1&hl=ko&itemlang=en-GB


    よく保存されているのが見えます.次の記事ではauth-serviceに関連付けて、実際の登録とログインを行います.

    リファレンス

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