Front-end/React
[React] boiler plate 로그인 & 회원가입
이안92
2021. 1. 7. 11:54
반응형
client/src
index.js
import 'react-app-polyfill/ie9';
import 'react-app-polyfill/ie11';
import 'core-js';
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App';
import * as serviceWorker from './serviceWorker';
import { BrowserRouter } from "react-router-dom";
import Reducer from './_reducers';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import ReduxThunk from 'redux-thunk';
const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk)(createStore);
ReactDOM.render(
<Provider
store={createStoreWithMiddleware(
Reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
)}
>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
client/src/_actions
types.js
export const LOGIN_USER = 'login_user';
export const REGISTER_USER = 'register_user';
export const AUTH_USER = 'auth_user';
export const LOGOUT_USER = 'logout_user';
user_actions.js
import axios from 'axios';
import {
LOGIN_USER,
REGISTER_USER,
AUTH_USER,
LOGOUT_USER,
} from './types';
import { USER_SERVER } from '../components/Config.js';
export function registerUser(dataToSubmit){
const request = axios.post(`${USER_SERVER}/register`,dataToSubmit)
.then(response => response.data);
return {
type: REGISTER_USER,
payload: request
}
}
export function loginUser(dataToSubmit){
const request = axios.post(`${USER_SERVER}/login`,dataToSubmit)
.then(response => response.data);
return {
type: LOGIN_USER,
payload: request
}
}
export function auth(){
const request = axios.get(`${USER_SERVER}/auth`)
.then(response => response.data);
return {
type: AUTH_USER,
payload: request
}
}
export function logoutUser(){
const request = axios.get(`${USER_SERVER}/logout`)
.then(response => response.data);
return {
type: LOGOUT_USER,
payload: request
}
}
client/src/_reducers
index.js
import { combineReducers } from 'redux';
import user from './user_reducer';
const rootReducer = combineReducers({
user,
});
export default rootReducer;
user_reducer.js
import {
LOGIN_USER,
REGISTER_USER,
AUTH_USER,
LOGOUT_USER,
} from '../_actions/types';
export default function(state={},action){
switch(action.type){
case REGISTER_USER:
return {...state, register: action.payload }
case LOGIN_USER:
return { ...state, loginSucces: action.payload }
case AUTH_USER:
return {...state, userData: action.payload }
case LOGOUT_USER:
return {...state }
default:
return state;
}
}
client/src/hoc
auth.js
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useEffect } from 'react';
import { auth } from '../_actions/user_actions';
import { useSelector, useDispatch } from "react-redux";
export default function (SpecificComponent, option, adminRoute = null) {
function AuthenticationCheck(props) {
let user = useSelector(state => state.user);
const dispatch = useDispatch();
useEffect(() => {
//To know my current status, send Auth request
dispatch(auth()).then(response => {
//Not Loggined in Status
if (!response.payload.isAuth) {
if (option) {
props.history.push('/login')
}
//Loggined in Status
} else {
//supposed to be Admin page, but not admin person wants to go inside
if (adminRoute && !response.payload.isAdmin) {
props.history.push('/')
}
//Logged in Status, but Try to go into log in page
else {
if (option === false) {
props.history.push('/')
}
}
}
})
}, [])
return (
<SpecificComponent {...props} user={user} />
)
}
return AuthenticationCheck
}
client/src/_components
App.js
import React, { Suspense } from 'react';
import { Route, Switch } from "react-router-dom";
import Auth from "../hoc/auth";
// pages for this product
import LandingPage from "./views/LandingPage/LandingPage.js";
import LoginPage from "./views/LoginPage/LoginPage.js";
import RegisterPage from "./views/RegisterPage/RegisterPage.js";
import NavBar from "./views/NavBar/NavBar";
import Footer from "./views/Footer/Footer"
//null Anyone Can go inside
//true only logged in user can go inside
//false logged in user can't go inside
function App() {
return (
<Suspense fallback={(<div>Loading...</div>)}>
<NavBar />
<div style={{ paddingTop: '69px', minHeight: 'calc(100vh - 80px)' }}>
<Switch>
<Route exact path="/" component={Auth(LandingPage, null)} />
<Route exact path="/login" component={Auth(LoginPage, false)} />
<Route exact path="/register" component={Auth(RegisterPage, false)} />
</Switch>
</div>
<Footer />
</Suspense>
);
}
export default App;
Config.js
//SERVER ROUTES
export const USER_SERVER = '/api/users';
client/src/_components/views/LandingPage
LandingPage.js
import React from 'react'
import { FaCode } from "react-icons/fa";
function LandingPage() {
return (
<>
<div className="app">
<FaCode style={{ fontSize: '4rem' }} /><br />
<span style={{ fontSize: '2rem' }}>Let's Start Coding!</span>
</div>
<div style={{ float: 'right' }}>Thanks For Using This Boiler Plate by John Ahn</div>
</>
)
}
export default LandingPage
client/src/_components/views/LoginPage
LoginPage.js
import React, { useState } from "react";
import { withRouter } from "react-router-dom";
import { loginUser } from "../../../_actions/user_actions";
import { Formik } from 'formik';
import * as Yup from 'yup';
import { Form, Icon, Input, Button, Checkbox, Typography } from 'antd';
import { useDispatch } from "react-redux";
const { Title } = Typography;
function LoginPage(props) {
const dispatch = useDispatch();
const rememberMeChecked = localStorage.getItem("rememberMe") ? true : false;
const [formErrorMessage, setFormErrorMessage] = useState('')
const [rememberMe, setRememberMe] = useState(rememberMeChecked)
const handleRememberMe = () => {
setRememberMe(!rememberMe)
};
const initialEmail = localStorage.getItem("rememberMe") ? localStorage.getItem("rememberMe") : '';
return (
<Formik
initialValues={{
email: initialEmail,
password: '',
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.email('Email is invalid')
.required('Email is required'),
password: Yup.string()
.min(6, 'Password must be at least 6 characters')
.required('Password is required'),
})}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
let dataToSubmit = {
email: values.email,
password: values.password
};
dispatch(loginUser(dataToSubmit))
.then(response => {
if (response.payload.loginSuccess) {
window.localStorage.setItem('userId', response.payload.userId);
if (rememberMe === true) {
window.localStorage.setItem('rememberMe', values.id);
} else {
localStorage.removeItem('rememberMe');
}
props.history.push("/");
} else {
setFormErrorMessage('Check out your Account or Password again')
}
})
.catch(err => {
setFormErrorMessage('Check out your Account or Password again')
setTimeout(() => {
setFormErrorMessage("")
}, 3000);
});
setSubmitting(false);
}, 500);
}}
>
{props => {
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
handleReset,
} = props;
return (
<div className="app">
<Title level={2}>Log In</Title>
<form onSubmit={handleSubmit} style={{ width: '350px' }}>
<Form.Item required>
<Input
id="email"
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Enter your email"
type="email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.email && touched.email ? 'text-input error' : 'text-input'
}
/>
{errors.email && touched.email && (
<div className="input-feedback">{errors.email}</div>
)}
</Form.Item>
<Form.Item required>
<Input
id="password"
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Enter your password"
type="password"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.password && touched.password ? 'text-input error' : 'text-input'
}
/>
{errors.password && touched.password && (
<div className="input-feedback">{errors.password}</div>
)}
</Form.Item>
{formErrorMessage && (
<label ><p style={{ color: '#ff0000bf', fontSize: '0.7rem', border: '1px solid', padding: '1rem', borderRadius: '10px' }}>{formErrorMessage}</p></label>
)}
<Form.Item>
<Checkbox id="rememberMe" onChange={handleRememberMe} checked={rememberMe} >Remember me</Checkbox>
<a className="login-form-forgot" href="/reset_user" style={{ float: 'right' }}>
forgot password
</a>
<div>
<Button type="primary" htmlType="submit" className="login-form-button" style={{ minWidth: '100%' }} disabled={isSubmitting} onSubmit={handleSubmit}>
Log in
</Button>
</div>
Or <a href="/register">register now!</a>
</Form.Item>
</form>
</div>
);
}}
</Formik>
);
};
export default withRouter(LoginPage);
client/src/_components/views/RegisterPage
RegisterPage.js
import React from "react";
import moment from "moment";
import { Formik } from 'formik';
import * as Yup from 'yup';
import { registerUser } from "../../../_actions/user_actions";
import { useDispatch } from "react-redux";
import {
Form,
Input,
Button,
} from 'antd';
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 16,
offset: 8,
},
},
};
function RegisterPage(props) {
const dispatch = useDispatch();
return (
<Formik
initialValues={{
email: '',
lastName: '',
name: '',
password: '',
confirmPassword: ''
}}
validationSchema={Yup.object().shape({
name: Yup.string()
.required('Name is required'),
lastName: Yup.string()
.required('Last Name is required'),
email: Yup.string()
.email('Email is invalid')
.required('Email is required'),
password: Yup.string()
.min(6, 'Password must be at least 6 characters')
.required('Password is required'),
confirmPassword: Yup.string()
.oneOf([Yup.ref('password'), null], 'Passwords must match')
.required('Confirm Password is required')
})}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
let dataToSubmit = {
email: values.email,
password: values.password,
name: values.name,
lastname: values.lastname,
image: `http://gravatar.com/avatar/${moment().unix()}?d=identicon`
};
dispatch(registerUser(dataToSubmit)).then(response => {
if (response.payload.success) {
props.history.push("/login");
} else {
alert(response.payload.err.errmsg)
}
})
setSubmitting(false);
}, 500);
}}
>
{props => {
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
handleReset,
} = props;
return (
<div className="app">
<h2>Sign up</h2>
<Form style={{ minWidth: '375px' }} {...formItemLayout} onSubmit={handleSubmit} >
<Form.Item required label="Name">
<Input
id="name"
placeholder="Enter your name"
type="text"
value={values.name}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.name && touched.name ? 'text-input error' : 'text-input'
}
/>
{errors.name && touched.name && (
<div className="input-feedback">{errors.name}</div>
)}
</Form.Item>
<Form.Item required label="Last Name">
<Input
id="lastName"
placeholder="Enter your Last Name"
type="text"
value={values.lastName}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.lastName && touched.lastName ? 'text-input error' : 'text-input'
}
/>
{errors.lastName && touched.lastName && (
<div className="input-feedback">{errors.lastName}</div>
)}
</Form.Item>
<Form.Item required label="Email" hasFeedback validateStatus={errors.email && touched.email ? "error" : 'success'}>
<Input
id="email"
placeholder="Enter your Email"
type="email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.email && touched.email ? 'text-input error' : 'text-input'
}
/>
{errors.email && touched.email && (
<div className="input-feedback">{errors.email}</div>
)}
</Form.Item>
<Form.Item required label="Password" hasFeedback validateStatus={errors.password && touched.password ? "error" : 'success'}>
<Input
id="password"
placeholder="Enter your password"
type="password"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.password && touched.password ? 'text-input error' : 'text-input'
}
/>
{errors.password && touched.password && (
<div className="input-feedback">{errors.password}</div>
)}
</Form.Item>
<Form.Item required label="Confirm" hasFeedback>
<Input
id="confirmPassword"
placeholder="Enter your confirmPassword"
type="password"
value={values.confirmPassword}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.confirmPassword && touched.confirmPassword ? 'text-input error' : 'text-input'
}
/>
{errors.confirmPassword && touched.confirmPassword && (
<div className="input-feedback">{errors.confirmPassword}</div>
)}
</Form.Item>
<Form.Item {...tailFormItemLayout}>
<Button onClick={handleSubmit} type="primary" disabled={isSubmitting}>
Submit
</Button>
</Form.Item>
</Form>
</div>
);
}}
</Formik>
);
};
export default RegisterPage
반응형