- vừa được xem lúc

Login trong Reactjs sử dụng axios với redux

0 0 46

Người đăng: Kính Gia Hà

Theo Viblo Asia

Giới thiệu

image.png

Cài đặt

Ở đây mình sẽ sử dụng them react-router-dom, material-ui và react-hook-form

npx create-react-app learn-reactjs
npm install --save react-hook-form
npm install --save react-router-dom
npm install @reduxjs/toolkit react-redux
npm install --save axios
cd learn-reactjs
npm start

Tạo các thư mục

learn-reactjs
├─ build
├─ node_modules
├─ public
├─ src
│ ├─ api
│ │ ├─ axiosClient.js │ │ └─ userApi.js
│ ├─ constants
│ │ └─ storage-keys.js
│ ├─ app
│ │ └─ store.js
│ ├─ components
│ │ └─ form-control
│ ├─ InputField
│ │ └─ index.jsx
│ │ └─ PasswordField
│ │ └─ index.jsx
│ └─ features │ └─ Auth
│ ├─ userSlice.js
│ ├─ components
│ │ ├─ Login.jsx │ │ └─ LoginForm.jsx │ ├─ page
│ │ └─ LoginPage.jsx │ └─ index.jsx
├─ App.css
├─ App.js
└─ index.js

App

Chỉnh sửa file index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import './index.css';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import store from './app/store'; ReactDOM.render( <React.StrictMode> <Provider store={store}> <BrowserRouter> <App /> </BrowserRouter> </Provider> </React.StrictMode>, document.getElementById('root')
); // If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Chỉnh sửa file app.js

import { Redirect, Route, Switch } from 'react-router-dom';
import './App.css';
import CounterFeature from './features/Counter'; function App() { return ( <div className="app"> <Switch> <Redirect from="home" to="/" exact /> <Route path="/login" component={LoginFeature}/> </Switch> </div> );
} export default App;

Form Control

InputField

Chỉnh sửa file index.jsx trong thư mục InputField

import { TextField } from '@material-ui/core';
import PropTypes from 'prop-types';
import React from 'react';
import { Controller } from "react-hook-form";
import { FormHelperText } from '@material-ui/core'; InputField.propTypes = { form: PropTypes.object.isRequired, name: PropTypes.string.isRequired, label: PropTypes.string, disabled: PropTypes.bool,
}; function InputField(props) { const { form, name, label, disabled } = props const { formState: { errors } } = form const hasError = errors[name] return ( <div> <Controller control={form.control} name={name} render={({ field }) => ( <TextField {...field} fullWidth margin="normal" variant="outlined" label={label} disabled={disabled} error={!!hasError} /> )} /> <FormHelperText error={!!hasError}> {errors[name]?.message} </FormHelperText> </div> );
} export default InputField;

PasswordField

Chỉnh sửa file index.jsx trong thư mục PasswordField

import { FormHelperText } from '@material-ui/core';
import FormControl from '@material-ui/core/FormControl';
import IconButton from '@material-ui/core/IconButton';
import InputAdornment from '@material-ui/core/InputAdornment';
import InputLabel from '@material-ui/core/InputLabel';
import OutlinedInput from '@material-ui/core/OutlinedInput';
import Visibility from '@material-ui/icons/Visibility';
import VisibilityOff from '@material-ui/icons/VisibilityOff';
import React, { useState } from 'react';
import { Controller } from "react-hook-form"; PasswordField.propTypes = { }; function PasswordField(props) { const { form, name, label, disabled } = props const { formState: { errors } } = form const hasError = errors[name] const [showPassword, setShowPassword] = useState(false) const toggleShowPassword = () => { setShowPassword(!showPassword) } return ( <div> <FormControl error={!!hasError} variant="outlined" margin="normal" fullWidth> <InputLabel htmlFor={name}>{label}</InputLabel> <Controller control={form.control} name={name} render={({ field }) => ( <OutlinedInput {...field} id={name} type={showPassword ? 'text' : 'password'} label={label} endAdornment={ <InputAdornment position="end"> <IconButton aria-label="toggle password visibility" onClick={toggleShowPassword} edge="end" > {showPassword ? <Visibility /> : <VisibilityOff />} </IconButton> </InputAdornment> } disabled={disabled} error={!!hasError} helperText={errors[name]?.message} labelWidth={70} /> )} /> <FormHelperText> {errors[name]?.message} </FormHelperText> </FormControl> </div> );
} export default PasswordField;

Constants

Chỉnh sửa file storage-keys.js

const StorageKeys = { user: 'user', access: 'access_token', refresh: 'refresh_token',
}
export default StorageKeys

Api

axiosClient

Bạn tạo một phiên bản axios mới với cấu hình tùy chỉnh bằng cách chỉnh sửa file axiosClient.js trong thư mục api.

import axios from 'axios'; const axiosClient = axios.create({ baseURL: 'http://127.0.0.1:8000/', headers: { 'content-type': 'application/json', }
})

userApi


import StorageKeys from "../constants/storage-keys";
import axiosClient from "./axiosClient"; const userApi = { register(data) { const url = 'register/'; return axiosClient.post(url, data); }, login(data) { const url = '/api/token/'; return axiosClient.post(url, data); }, async getUser(params) { const newParams = { ...params } const accessToken = localStorage.getItem(StorageKeys.access) const url = `users/`; const response = await axiosClient.get(url, { params: { ...newParams }, headers: { Authorization: `Bearer ${accessToken}` } }); return response }, async getProfile(params) { const newParams = { ...params } const accessToken = localStorage.getItem(StorageKeys.access) const response = await axiosClient.get(`/detail/`, { params: { ...newParams }, headers: { Authorization: `Bearer ${accessToken}` } }) return response },
} export default userApi

Auth

Tạo một slice user state cho Redux

Chỉnh sửa file userSlice.js trong thư mục Auth

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import userApi from '../../api/userApi';
import StorageKeys from '../../constants/storage-keys'; // createAsyncThunk cái này sử dụng cho login và register
export const register = createAsyncThunk( 'users/register', async (payload) => { //call api to register return data; }
) // createAsyncThunk cái này sử dụng cho login và register
export const login = createAsyncThunk( 'users/login', async (payload) => { try { const response = await authApi.login(payload); localStorage.setItem(StorageKeys.access, response.data.access); localStorage.setItem(StorageKeys.refresh, response.data.refresh); const username = JSON.parse(response.config.data).username const responseUser = await authApi.getUser({ username: username }) const user = {...responseUser.data[0]} const responseProfile = await authApi.getProfile({user: user.id}) const profile = {...responseProfile.data} const data = { ...user, ...profile, } localStorage.setItem(StorageKeys.user, JSON.stringify(data)); return data } catch (error) { console.log(error) return error.message; } }
) const userSlice = createSlice({ name: 'user', initialState: { current: JSON.parse(localStorage.getItem(StorageKeys.USER)) || {}, settings: {}, }, reducers: { logout(state) { //clear local storage state.current = {} localStorage.removeItem(StorageKeys.access) localStorage.removeItem(StorageKeys.refresh) localStorage.removeItem(StorageKeys.user) } }, extraReducers: { [register.fulfilled]: (state, action) => { state.current = action.payload; }, [login.fulfilled]: (state, action) => { state.current = action.payload; } }
}) const { actions, reducer } = userSlice
export const {logout} = actions
export default reducer

Store

Tạo một Redux Store

Tạo một Redux Store bằng cách chỉnh sửa file store.js trong thư mục store

import userReducer from '../features/Auth/userSlice' const { configureStore } = require("@reduxjs/toolkit"); const rootReducer = { user: userReducer,
} const store = configureStore({ reducer: rootReducer,
}) export default store

Login

Chỉnh sửa file index.jsx trong thư mục Auth

import React from 'react';
import { Route, Switch, useRouteMatch } from 'react-router-dom';
import LoginPage from './page/LoginPage';
import { Box } from '@material-ui/core'; LoginFeature.propTypes = { }; function LoginFeature(props) { const match = useRouteMatch() return ( <div> <Box pt={4}> <Switch> <Route path={match.url} component={LoginPage} exact /> </Switch> </Box> </div> );
} export default LoginFeature;

Chỉnh sửa file LoginPage.jsx trong thư mục Auth/page

import { makeStyles } from '@material-ui/core';
import React from 'react';
import { useSelector } from 'react-redux';
import LoginForm from '../components/LoginForm'; const useStyles = makeStyles((theme) => ({ root: { padding: theme.spacing(4, 50), },
})) LoginPage.propTypes = { }; function LoginPage(props) { const classes = useStyles(); const loginInUser = useSelector(state => state.user.current) const isLoggedIn = !!loginInUser.id return ( <div className={classes.root}> {!isLoggedIn && ( <> <LoginForm /> </> )} {isLoggedIn && ( <div> <h2>Is Login</h2> </div> )} </div> );
} export default LoginPage;

Chỉnh sửa file Login.jsx

import { unwrapResult } from '@reduxjs/toolkit';
import PropTypes from 'prop-types';
import React from 'react';
import { useDispatch } from 'react-redux';
import { login } from '../../userSlice';
import LoginForm from '../LoginForm'; Login.propTypes = {
}; function Login(props) { const dispatch = useDispatch() const handleSubmit = async (values) => { try { const actions = login(values) await dispatch(actions) } catch (error) { console.log(error) } } return ( <div> <LoginForm onSubmit={handleSubmit} /> </div> );
} export default Login;

Chỉnh sửa file LoginForm.jsx

import { yupResolver } from '@hookform/resolvers/yup';
import { Avatar, Button, LinearProgress, makeStyles, Typography } from '@material-ui/core';
import LockOutlined from '@material-ui/icons/LockOutlined';
import PropTypes from 'prop-types';
import React from 'react';
import { useForm } from 'react-hook-form';
import * as yup from "yup";
import InputField from '../../../../components/form-control/InputField';
import PasswordField from '../../../../components/form-control/PasswordField'; LoginForm.propTypes = { onSubmit: PropTypes.func,
}; const useStyles = makeStyles((theme) => ({ root: { padding: theme.spacing(2, 2), }, avatar: { margin: "0 auto 15px", backgroundColor: theme.palette.secondary.main, }, title: { textAlign: "center", }, submit: { margin: theme.spacing(2, 0, 0, 0), }, linearProgress: { margin: theme.spacing(0, 0, 4, 0) }
})) function LoginForm(props) { const classes = useStyles(); const { onSubmit } = props const schema = yup.object().shape({ identifier: yup.string() .required("Please enter your email.") .email("Please enter a valid email"), password: yup.string() .required("Please enter your password.") }); const form = useForm({ defaultValues: { identifier: '', password: '', }, resolver: yupResolver(schema), }) const handleSubmit = async (values) => { if (onSubmit) { await onSubmit(values) } } const { isSubmitting } = form.formState return ( <div className={classes.root}> {isSubmitting && <LinearProgress className={classes.linearProgress} color="secondary" />} <Avatar className={classes.avatar}> <LockOutlined /> </Avatar> <Typography component="h3" variant="h5" className={classes.title}> Sign In </Typography> <form onSubmit={form.handleSubmit(handleSubmit)}> <InputField name="identifier" label="Email" form={form} /> <PasswordField name="password" label="Password" form={form} /> <Button disabled={isSubmitting} type="submit" variant="contained" color="primary" fullWidth className={classes.submit}> Sign in </Button> </form> </div> );
} export default LoginForm;

Bài viết đến đây là kết thúc. Chúc các bạn thành công

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tìm hiểu về Redux Thunk

Chào mọi người, nếu bạn là người đã biết về React và đang làm quen với Redux chắc hẳn bạn đang rất mơ hồ về các khái niệm cơ bản của Redux như dispatch, store, action creator,... bạn còn đang vật lộn với đống document của Redux để hiểu những khái niệm đó và bạn nghe ai đó trong team nói về Redux Thu

0 0 371

- vừa được xem lúc

[React] Giới thiệu tổng quát về Redux Toolkit

1. Redux Toolkit (RTK) là gì và tại sao lại có nó. . .

0 0 6.6k

- vừa được xem lúc

Hướng dẫn React Redux cho người mới bắt đầu - Phần 1

Lời mởi đầu. Chào các bạn, ở thời điểm thực hiện bài viết này mình cũng là một người đang bắt đầu tìm hiểu và học với ReactJs và Redux, trong quá trình tìm hiểu đọc các tài liệu về thư viện này mình có tìm được một bài hướng dẫn khá hay nên đã quyết định chia sẻ với mọi người .

0 0 262

- vừa được xem lúc

Redux Main Concept

. Xin chào các bạn, cũng khá lâu rồi mới quay lại với series React JS của mình. Ngày hôm nay mình sẽ chia sẻ về Redux.

0 0 28

- vừa được xem lúc

Làm quen với Redux để quản lý state cho ứng dụng Javascript

1. Giới thiệu. Redux là một thư viện giúp bạn quản lý trạng thái (state) cho các ứng dụng javascript (kể cả js thuần). Redux ra đời lấy cảm hứng từ tư tưởng của ngôn ngữ Elm và kiến trúc Flux của Facebook.

0 0 19

- vừa được xem lúc

Redux cho người mới bắt đầu - Part 1 Introduction

Hiện nay Reactjs là một thư viện mạnh mẽ khá phổ biến. Khi làm việc với React hay các dự án ứng dụng Single Page nói chung, có một vấn đề khá đau đầu là làm sao quản lý được trạng thái của ứng dụng đó

0 0 36