UserContextを使用し、UserReducerを使用してログインを実施
77763 ワード
useContextとuseReducerを使用してユーザーログインを検証する
#作業前フォルダ構造
map配線 を用いる. App.jsファイルにルータ を構成する AutheStateContext:このコンテキストには、認証トークンとユーザーの詳細が含まれます. AutheDispatchContext:このコンテキストでは、ユーザモニタ管理ステータスを使用して後で生成される コンテキストオブジェクトのコンテキストを作成します.jsファイルに次の内容を追加します.
電子メールとパスワード入力フィールドのステータスと入力ハンドルを定義します.
ログインコンポーネントにhandleLogin関数を作成します.
登録テスト
この問題を解決するには、個人パス(認証されたユーザーのみがアクセスできるパス)を定義し、親コンポーネントを作成して、ユーザーが認証されたときに適切なコンポーネントをレンダリングする必要があります.それ以外の場合は、ログインページにリダイレクトされます.
まず、パス構成のisPrivateパスがプライベートパスであるかどうかを指定するプロパティを追加します.
私たちのアプリケーションの2つのパスは、ダッシュボードページと404ページです.
Approuteコンポーネントに次の内容を追加できます.
#作業前フォルダ構造
*AppRoute-ユーザーのアクセスを許可するコンポーネントのみ(ログインしていない場合はリダイレクト)
* action.js-論理を構成するフックの位置を初期化するために必要な様々なコンテキストオブジェクト
* reducer.js-初期状態または所望状態を管理する減速機
# 1. プロジェクトの作成とインストールに必要なライブラリ
npx create-react-app login-auth
yarn add react-router-dom
yarn add axios
# 2. フォルダを作成し、各ページコンポーネントを作成します(上図のフォルダルートディレクトリを参照)。
1-1. login.js
// pages/login/index.js
import React from 'react';
import styles from './login.module.css';
function Login(props) {
return (
<div className={styles.container}>
<div className={styles.formContainer}>
<h1>Login Page</h1>
<form>
<div className={styles.loginForm}>
<div className={styles.loginFormItem}>
<label htmlFor='email'>Username</label>
<input type='text' id='email' />
</div>
<div className={styles.loginFormItem}>
<label htmlFor='password'>Password</label>
<input type='password' id='password' />
</div>
</div>
<button>login</button>
</form>
</div>
</div>
);
}
export default Login;
1-2. login.css
// pages/login/login.module.css
.container {
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.formContainer {
width: 200px;
}
.error {
font-size: 0.8rem;
color: #bb0000;
}
.loginForm {
display: flex;
flex-direction: column;
}
.loginFormItem {
display: flex;
flex-direction: column;
margin-bottom: 10px;
}
2-1. dashboard.js
// pages/dashboard/index.js
import React from 'react'
import styles from './Dashboard.module.css'
function Dashboard(props) {
return (
<div style={{ padding: 10 }}>
<div className={styles.dashboardPage} >
<h1>
Dashboard
</h1>
<button className={styles.logoutBtn} >Logout</button>
</div>
<p>Welcome to the dashboard</p>
</div>
)
}
export default Dashboard
2-2. dashboard.css
// pages/dashboard/dashboard.module.css
.logoutBtn {
height: '30px';
width: '100px';
}
.dashboardPage {
display: flex;
width: 100%;
justify-content: space-between;
}
3-1. dashboard.js
// pages/notFound/index.js
import React from 'react';
import styles from './notfound.module.css';
function NotFound(props) {
return (
<div className={styles.container}>
<h1>Page not found</h1>
</div>
);
}
export default NotFound;
3-2. dashboard.css
// pages/notFound/notfound.module.css
.container {
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
# 3. ルーティングの設定
npx create-react-app login-auth
yarn add react-router-dom
yarn add axios
// pages/login/index.js
import React from 'react';
import styles from './login.module.css';
function Login(props) {
return (
<div className={styles.container}>
<div className={styles.formContainer}>
<h1>Login Page</h1>
<form>
<div className={styles.loginForm}>
<div className={styles.loginFormItem}>
<label htmlFor='email'>Username</label>
<input type='text' id='email' />
</div>
<div className={styles.loginFormItem}>
<label htmlFor='password'>Password</label>
<input type='password' id='password' />
</div>
</div>
<button>login</button>
</form>
</div>
</div>
);
}
export default Login;
// pages/login/login.module.css
.container {
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.formContainer {
width: 200px;
}
.error {
font-size: 0.8rem;
color: #bb0000;
}
.loginForm {
display: flex;
flex-direction: column;
}
.loginFormItem {
display: flex;
flex-direction: column;
margin-bottom: 10px;
}
// pages/dashboard/index.js
import React from 'react'
import styles from './Dashboard.module.css'
function Dashboard(props) {
return (
<div style={{ padding: 10 }}>
<div className={styles.dashboardPage} >
<h1>
Dashboard
</h1>
<button className={styles.logoutBtn} >Logout</button>
</div>
<p>Welcome to the dashboard</p>
</div>
)
}
export default Dashboard
// pages/dashboard/dashboard.module.css
.logoutBtn {
height: '30px';
width: '100px';
}
.dashboardPage {
display: flex;
width: 100%;
justify-content: space-between;
}
// pages/notFound/index.js
import React from 'react';
import styles from './notfound.module.css';
function NotFound(props) {
return (
<div className={styles.container}>
<h1>Page not found</h1>
</div>
);
}
export default NotFound;
// pages/notFound/notfound.module.css
.container {
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
// Config/routes.js
import React from 'react'
import Login from '../pages/login/index'
import DashBoard from '../pages/dashBoard/index'
import PageNotFound from '../pages/pageNotFound/index'
const routes =[
{
path:'/',
component: Login
},
{
path:'/dashboard',
component: Dashboard
},
{
path:'/*',
component: PageNotFound
},
]
export default routes
// App.js
import React from 'react';
import {
BrowserRouter as Router,
Redirect,
Route,
Switch,
} from 'react-router-dom';
import routes from './Config/routes.js';
function App() {
return (
<Router>
<Switch>
{routes.map((route) => (
<Route
exact
key={route.path}
path={route.path}
component={route.component}
/>
))}
</Switch>
</Router>
);
}
export default App;
# 4. 検証コンテキストの設定
dispatch
が渡されます.これにより、必要なコンポーネントにdispatch
を簡単に提供できます.// Context/context.js
import React,{useContext,createContext,useReducer} from "react";
import {AuthReducer,initialState} from './reducer'
const AuthStateContext = createContext(null)
const AuthDispatchContext = createContext(null)
useAuthDispatch
、useAuthDispatch
フックおよびproviderは、同じファイル(context.js)で生成される.
// Context/context.js
[...]
export function useAuthState() {
const context = React.useContext(AuthStateContext);
if (context === undefined) {
throw new Error("useAuthState는 AuthProvider 안에서만 사용 가능합니다.")
}
return context;
}
export function useAuthDispatch() {
const context = React.useContext(AuthDispatchContext);
if (context === undefined) {
throw new Error("useAuthDispatch는 AuthProvider 안에서만 사용 가능합니다.")
}
return context;
}
export const AuthProvider =({children})=>{
const [user,dispatch] = useReducer(AuthReducer,initialState)
return(
<AuthStateContext.Provider value={user}>
<AuthDispatchContext.Provider value={dispatch}>
{children}
</AuthDispatchContext.Provider>
</AuthStateContext.Provider>
)
}
このようにして減速機を作っていないので、エラーが発生します.reduce,actionを作成する1-1. reducer.js
// Context/reducer.js
let user = localStorage.getItem('currentUser')? JSON.parse(localStorage.getItem('currentUser')).user : '';
let token = localStorage.getItem('currentUser')? JSON.parse(localStorage.getItem('currentUser')).auth_token : '';
export const initialState ={
user:""||user,
token:""||token,
loading:false,
errorMessage:null
}
export const AuthReducer =(initialState,action)=>{
switch (action.type){
case 'REQUEST_LOGIN':
return{
...initialState,
loading: true
}
case 'LOGIN_SUCCESS':
return{
...initialState,
user:action.payload.user,
token:action.payload.auth_token,
loading: false
}
case 'LOGOUT':
return{
...initialState,
user:'',
token:''
}
case 'LOGIN_ERROR':
return{
...initialState,
loading: false,
errorMessage: action.error
}
default:
throw new Error( `Unhandled action type: ${action.type}`)
}
}
1-2. action.js
// Context/action.js
import axios from "axios";
const ROOT_URL = 'https://secret-hamlet-03431.herokuapp.com';
export const loginUser=async (dispatch,loginPayload)=>{
const requestOptions={
url:`${ROOT_URL}/login`,
method: 'POST',
headers: {
'Content-Type':'application/json'
},
data:loginPayload
}
try{
const response = await axios(requestOptions)
if(response.status ===200){
dispatch({type:'LOGIN_SUCCESS',payload:response.data})
localStorage.setItem('currentUser',JSON.stringify(response.data))
return response.data
}else{
dispatch({type:'LOGIN_ERROR',error:response.data.error[0]})
}
return ;
}catch (e){
dispatch({type:'LOGIN_ERROR',error:e})
}
}
export async function logout(dispatch) {
dispatch({ type: 'LOGOUT' });
localStorage.removeItem('currentUser');
localStorage.removeItem('token');
}
これで、UserReducerを使用してコンテキストとステータス管理のすべての設定が完了し、Contextフォルダのすべてのコンテンツをエクスポートするフォルダにインデックスが作成されます.jsファイルを生成します.import {loginUser,logout,axiosLoginUser} from './actions'
import {AuthProvider,useAuthState,useAuthDispath} from './context'
export {loginUser,logout,useAuthDispath,useAuthState,AuthProvider,axiosLoginUser}
# 5. コンテキストの統合
// App.js
import routes from './Config/routes.js';
import { AuthProvider } from "./Context";
function App() {
return (
<AuthProvider>
<Router>
<Switch>
{routes.map((route) => (
<Route
exact
key={route.path}
path={route.path}
component={route.component}
/>
))}
</Switch>
</Router>
</AuthProvider>
);
}
export default App;
# 6. ログイン・ページ・コンポーネントでの認証の実施
電子メールとパスワード入力フィールドのステータスと入力ハンドルを定義します.
// pages/login/index.js
[...]
function Login(props) {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
return (
<div className={styles.container}>
<div className={{ width: 200 }}>
<h1>Login Page</h1>
<form >
<div className={styles.loginForm}>
<div className={styles.loginFormItem}>
<label htmlFor="email">Username</label>
<input type="text" id='email' value={email} onChange={(e) => setEmail(e.target.value)} />
</div>
<div className={styles.loginFormItem}>
<label htmlFor="password">Password</label>
<input type="password" id='password' value={password} onChange={(e) => setPassword(e.target.value)} />
</div>
</div>
<button>login</button>
</form>
</div>
</div>
)
}
[...]
このとき、サーバへの送信を処理するために使用できるステータスの電子メールとパスワードフィールドを含む関数を作成できます.handleLogin
関数呼び出しを作成し、login
ボタン呼び出しをクリックします.ログインコンポーネントにhandleLogin関数を作成します.
// pages/login/index.js
[...]
import {useAuthState,useAuthDispath,axiosLoginUser} from '../../context'
[...]
function Login(props) {
const dispatch = useAuthDispath()
const [email,setEmail] =useState('')
const [password,setPassword] =useState('')
const {loading} = useAuthState() //얘는 initialState안에 있는 얘들 구조분해 할당
const handleLogin =async (e)=>{
e.preventDefault()
let payload = {email,password}
try{
const response = await axiosLoginUser(dispatch,payload)
if(!response.user) return
props.history.push('/dashboard')
}catch (e){
console.error(e)
}
}
return (
<div className={styles.container}>
<div className={styles.formContainer}>
<h1>Login Page</h1>
<form >
<div className={styles.loginForm}>
<div className={styles.loginFormItem}>
<label htmlFor="email">UserName</label>
<input type="text" id={'email'} value={email} onChange={(e)=>setEmail(e.target.value)} disabled={loading}/>
</div>
<div className={styles.loginFormItem}>
<label htmlFor="password">Password</label>
<input type="password" id={'password'} value={password} onChange={(e)=>setPassword(e.target.value)} disabled={loading}/>
</div>
</div>
<button onClick={handleLogin}>login</button>
</form>
</div>
</div>
);
}
export default Login;
登録テストemail: [email protected]
、登録テスト
password: admin123
#7.ダッシュボードでのログアウトの実施
// pages/dashboard/index.js
import React from 'react'
import styles from './dashBoard.module.css'
import {useAuthState,logout,useAuthDispath} from "../../context";
function Dashboard(props) {
const dispatch = useAuthDispath()
const userDetails = useAuthState() // 얘는 initialState
const handleLogout=()=>{
logout(dispatch)
props.history.push('/login')
}
return (
<div style={{ padding: 10 }}>
<div className={styles.dashboardPage} >
<h1>
Dashboard
</h1>
<button className={styles.logoutBtn} onClick={handleLogout}>Logout</button>
</div>
<p>Welcome {userDetails.user.email}</p>
</div>
)
}
export default Dashboard
これにより、ログインとログアウトを簡単に作成できますが、ユーザーが認証を受けていない場合でも、ダッシュボードパスと同じパスにアクセスし続ける可能性があります.#8.パス保護の検証
この問題を解決するには、個人パス(認証されたユーザーのみがアクセスできるパス)を定義し、親コンポーネントを作成して、ユーザーが認証されたときに適切なコンポーネントをレンダリングする必要があります.それ以外の場合は、ログインページにリダイレクトされます.
まず、パス構成のisPrivateパスがプライベートパスであるかどうかを指定するプロパティを追加します.
私たちのアプリケーションの2つのパスは、ダッシュボードページと404ページです.
// Config/routes.js
[…]
const routes = [
{
path: '/login',
component: Login,
isPrivate: false,
},
{
path: '/dashboard',
component: Dashboard,
isPrivate: true,
},
{
path: '/*',
component: NotFound,
isPrivate: true,
},
];
export default routes;
次に、パスの保護に役立つコンポーネントを作成します.componentsフォルダを作成し、AppRoutes.js
ファイルを作成します.Approuteコンポーネントに次の内容を追加できます.
// components/AppRoute.js
import React from 'react';
import {Redirect,Route} from 'react-router-dom'
import { useAuthState} from "../context";
function AppRoute({component:Component,path,isPrivate,...rest}) {
const userDetails = useAuthState()
return (
<Route
exact
path={path}
render={props =>
isPrivate && !Boolean(userDetails.token) ? (
<Redirect to={{pathname: '/'}}/>
) : (
<Component {...props}/>
)
}
{...rest}
/>
);
}
export default AppRoute;
#8. App.jsの変更
// App.js
function App() {
return (
<AuthProvider>
<Router>
<Switch>
{routes.map((route) => (
<AppRoute
exact
key={route.path}
path={route.path}
component={route.component}
isPrivate={route.isPrivate}
/>
))}
</Switch>
</Router>
</AuthProvider>
);
}
export default AppRoute;
そして今実行してテストすればいいです.Reference
この問題について(UserContextを使用し、UserReducerを使用してログインを実施), 我々は、より多くの情報をここで見つけました https://velog.io/@shin6403/useContextuseReducer로-로그인-구현하기テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol