Create Register & Login using ASP Core 2.1 + React (part 2)

min read

Tiếp tục bài viết trước mình xây dựng BackEnd xử lý Login & Register trong ASP Core 2.1, trong phần tiếp theo này mình sẽ xây dựng FrontEnd, bạn nào chưa xem phần kia, thì xem lại tại đây

+ Create Register & Login using ASP Core 2.1 + React (part 1)

Xây dựng Front-End trong React

Sau khi xem và tạo được project ASP Core 2.1 đồng thời đã cấu hình chuỗi các sự kiển xử lý trong ASP, Thì chúng mình tiếp tục cài đặt React
Bạn mở Cmd lên và trỏ đến thư mục ClientApp trong thư mục project ASP, để cài đặt một số thư viện cần thiết sau đây!
tiến hành cái một số plugin Sau
    "@material-ui/core": "^4.11.0",
    "@material-ui/icons": "^4.9.1",
    "@material-ui/lab": "^4.0.0-alpha.56",
    "axios": "^0.21.0",
    "react-router-dom": "^4.3.1",

Bạn mở cmd lên và gõ lệnh bên dưới đây để cài:
npm install @material-ui/core @material-ui/icons @material-ui/lab axios react-router-dom

Trong plugin trên mình dùng material-ui để xây giao diện cho project, bạn nào chưa dùng qua các bạn có thể vào trang chủ của material-ui để tìm hiểu thêm và sử dụng nhé
Bạn nhìn hình minh họa project của mình như sau

Create Register & Login using ASP Core 2.1 + React (part 2)

Okay, giờ ta xây dựng FrontEnd cho React thôi
Tạo đường dẫn sau: ClientApp/src/api và tạo file apiService.js, cấu hình function xử lý request api đến ASP Core

import axios from 'axios';
let API_URL="https://localhost:44382/api";
export default function callApi(endpoint, method='GET',body){
    return axios({
        method,
        url:`${API_URL}/${endpoint}`,
        data:body,
    }).catch(e=>{
        console.log(e)
    })
}

Tạo đường dẫn như sau, để tạo các file cấu hình layout component cho project: ClientApp/components/ 
+ ClientApp/components/Home.js

import React,{useEffect,useState} from 'react'
import { makeStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
import Alert from '@material-ui/lab/Alert';
import { Redirect } from 'react-router-dom';
const useStyles = makeStyles((theme) => ({
  root: {
    flexGrow: 1,
    marginTop:20
  },
  paper:{
    width:'100%',
    margin:'auto'
  },
  link:{
    padding:10,
    display:'inline-block'
  }
}));
export default function Home() {
  const classes = useStyles();
  const [name,setName] = useState()
  const [email,setEmail]= useState()
  var login = sessionStorage.getItem("login");
  if(login==="false" || login===null){
      return <Redirect to={{pathname: "/login"}}/>
  }
  useEffect(() => {
    var name = sessionStorage.getItem("name");
    var email = sessionStorage.getItem("email");
    setName(name)
    setEmail(email)
  });
  return (
    <div className={classes.root}>
         <Grid container spacing={3}>
            <Grid item xs={12}>
              <Paper className={classes.paper}>
                  <Alert severity="success">Hello {name}, Email:{email}</Alert>
              </Paper>
            </Grid>
        </Grid>
    </div>
  )
}

Đoạn code trên mình cần import một số thư viện material để viết các component mà material hổ trợ cho ta, bạn sài thẻ component nào thì import thư viện đó vào

import { makeStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
import Alert from '@material-ui/lab/Alert';

Bạn chú ý mình có dùng useEffect,useState trong file, để thiết lập trạng của ứng dụng, để sài được (useState,useEffect) ta cần chỉnh sửa thư viện bên dưới vào file
import React,{useEffect,useState} from 'react'
Mình cần set giá trị trạng thái ban đầu cho thẻ input(Email,Password) làm vậy để kiểm tra giá trị của từng field
const [name,setName] = useState()
const [email,setEmail]= useState()

Xử lý kiểm tra xem, người dùng có đăng nhập (login) chưa, chúng ta cần xét điều kiện sau đây, kiểm tra giá trị dưới local 

Create Register & Login using ASP Core 2.1 + React (part 2)

var login = sessionStorage.getItem("login");
  if(login==="false" || login===null){
      return <Redirect to={{pathname: "/login"}}/>
  }
  useEffect(() => {
    var name = sessionStorage.getItem("name");
    var email = sessionStorage.getItem("email");
    setName(name)
    setEmail(email)
  });

Cuối cùng ta chỉ cần hiển thị thông tin người dùng ra thôi
<Alert severity="success">Hello {name}, Email:{email}</Alert>

+ ClientApp/components/Login.js

import React,{useState,useEffect}from 'react'
import { makeStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
import ButtonBase from '@material-ui/core/ButtonBase';
import Typography from '@material-ui/core/Typography';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import callApi from '../api/apiService';
import { Redirect } from 'react-router-dom';
const useStyles = makeStyles((theme) => ({
    root: {
        flexGrow: 1,
        marginTop:50
      },
      paper: {
        padding: theme.spacing(2),
        margin: 'auto',
        maxWidth: 500,
      },
      form: {
        width: '100%', // Fix IE 11 issue.
        marginTop: theme.spacing(1),
      },
      title:{
        fontSize:30,
        textAlign:'center'
      },
      image: {
        width: 250,
        height: 100,
        margin: 'auto',
        display: 'block',
      },
      img: {
        margin: 'auto',
        display: 'block',
        maxWidth: '100%',
        maxHeight: '100%',
      },
      txtInput:{
        width:'100%',
        marginBottom:10
      },
      
   
  }));
export default function Login() {
    const classes = useStyles();
    const [login,setLogin] = useState(false)
    const [email,setEmail] = useState();
    const [password,setPassword] = useState();
    useEffect(() => {
       if(sessionStorage.getItem("login")){
         setLogin(true)
       }
    })
    const changeEmail = (e)=>{
       setEmail(e.target.value);
    }
    const changePass = (e)=>{
      setPassword(e.target.value);
    }
    const btnLogin = (e)=>{
      e.preventDefault();
     if(email!=="" && password!==""){
         let data = {
           "Email":email,
           "Password":password
         }
         callApi(`users/login`,'POST',data).then(item=>{
             if(item.data.idUser>0){
                alert("Login successfuly");
                sessionStorage.setItem("login",true);
                sessionStorage.setItem("name", item.data.name);
                sessionStorage.setItem("email", item.data.email);
                setLogin(true)
              }
              else{
                 alert("Login error!")
              }
         })
      }
    }
    if(login){
      return <Redirect to={{pathname: "/home"}}/>
    }
    return (
        <div className={classes.root}>
            <Grid container spacing={3}>
                <Grid item xs={12}>
                <Paper className={classes.paper}>
                    <ButtonBase className={classes.image}>
                    <img className={classes.img} alt="complex" src="logo-top-new.svg" />
                    </ButtonBase>
                    <Typography className={classes.title} variant="h4">
                                Login Hoanguyen IT
                    </Typography>    
                    <form className={classes.form} noValidate>
                    <Grid item xs={12} sm container>
                        <Grid item xs={12}>
                            <TextField id="Email" label="Email" type="email" variant="outlined" className={classes.txtInput} size="small" onChange={changeEmail}/>
                        </Grid>
                        <Grid item xs={12}>
                            <TextField id="Password"  type="password" label="Password" variant="outlined" className={classes.txtInput} size="small" onChange={changePass}/>
                        </Grid>
                        <Grid item xs={12}>
                            <Button type="submit" fullWidth variant="contained" color="primary" className={classes.submit} onClick={btnLogin} >
                                Log in
                            </Button>
                        </Grid>
                    </Grid>
                    </form>
                    
                </Paper>
                </Grid>
            </Grid>
        </div>
    )
}

Trong đoạn code Login trên ta cần xử lý Login như sau: 
Phần layout đăng nhập thì chúng ta cần sử dụng các thẻ mà material cung cấp cho ta , mà để sử dụng được nó thì cần phải khai báo thư viện đó vào như trong đoạn code bên trên mình có khai báo
Sử dụng (useEffect,useState) để xử lý thay đổi trạng thái của các field trong thẻ TextField
sử dụng useEffect để kiểm tra xem, session người dùng còn lưu trữ khi đăng nhập vào hệ thống không, nếu còn thì trạng thái setLogin(true) sao đó xử lý Redirect tên component Home

const [login,setLogin] = useState(false)
const [email,setEmail] = useState();
const [password,setPassword] = useState();
useEffect(() => {
       if(sessionStorage.getItem("login")){
         setLogin(true)
       }
})
if(login){
      return <Redirect to={{pathname: "/home"}}/>
}

Xử lý thay đổi nhập liệu trong thẻ TextField

const changeEmail = (e)=>{
       setEmail(e.target.value);
}
const changePass = (e)=>{
      setPassword(e.target.value);
}

Bắt sự kiện btnLogin khi người dụng đăng nhập vào hệ thống, ta tiến hành lưu các thông tin cần thiết. Để gọi được api ta cần import 

import callApi from '../api/apiService'; //call api 

const btnLogin = (e)=>{
      e.preventDefault();
     if(email!=="" && password!==""){
         let data = {
           "Email":email,
           "Password":password
         }
         callApi(`users/login`,'POST',data).then(item=>{
             if(item.data.idUser>0){
                alert("Login successfuly");
                sessionStorage.setItem("login",true);
                sessionStorage.setItem("name", item.data.name);
                sessionStorage.setItem("email", item.data.email);
                setLogin(true)
              }
              else{
                 alert("Login error!")
              }
         })
      }
    }

+ ClientApp/components/Register.js

import React,{useState} from 'react'
import callApi from '../api/apiService'
import { makeStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
import ButtonBase from '@material-ui/core/ButtonBase';
import Typography from '@material-ui/core/Typography';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import Alert from '@material-ui/lab/Alert';
import { Redirect } from 'react-router-dom';
const useStyles = makeStyles((theme) => ({
  root: {
    flexGrow: 1,
    marginTop:20
  },
  paper: {
    padding: theme.spacing(2),
    margin: 'auto',
    maxWidth: 600,
  },
  form: {
    width: '100%', // Fix IE 11 issue.
    marginTop: theme.spacing(1),
  },
  title:{
    fontSize:30,
    textAlign:'center'
  },
  image: {
    width: 250,
    height: 100,
    margin: 'auto',
    display: 'block',
  },
  img: {
    margin: 'auto',
    display: 'block',
    maxWidth: '100%',
    maxHeight: '100%',
  },
  txtInput:{
    width:'98%',
    margin:'1%'
  },
  submit: {
    margin: theme.spacing(3, 0, 2),
  },
  AlertError:{
    padding:"2px",
    marginBottom:2,
    color:'#ec3237'
  }
}));
export default function Register() {
  const classes = useStyles();
  const [success,setSuccess] = useState(false)
  /* set error alert submit form */
  const [error,setError] = useState(true)
  const [msgError,setMsgError] = useState(null);
  /*SET attribute in Form*/
  const ConfigData = Object.freeze({
    Name:"",
    Avatar:"",
    Age:0,
    Password: "",
    Email:"",
    Address:"",
    BirthDay:""
  });

  /*SET Valid attribute*/
  const ConfigDataValid = Object.freeze({
    NameValid:null,
    AvatarValid:null,
    AgeValid:null,
    PasswordValid: null,
    EmailValid:null,
    AddressValid:null,
    BirthDayValid:null
  })
  const [infoData, updateInfoData] = React.useState(ConfigData);
  const [infoDataValid, updateInfoDataValid] = React.useState(ConfigDataValid);
  const handleChange = (e) => {
       checkDataValid(e);
  };
  const checkDataValid=(e)=>{
    if(e.target.value.trim()===""){
      updateInfoDataValid({...infoDataValid,[e.target.name+"Valid"]:true})
      
    }
    else{
        updateInfoDataValid({...infoDataValid,[e.target.name+"Valid"]:false})
        if(e.target.name!=="Avatar"){
          updateInfoData({
            ...infoData,
            [e.target.name]:e.target.value.trim(), 
          });
        }else{
          updateInfoData({
            ...infoData,
            [e.target.name]:e.target.files[0] , 
          });
        }
    }
  }
 
  const btnRegister = (e) => {
    e.preventDefault();
      if(!infoDataValid.NameValid && !infoDataValid.AgeValid && 
        !infoDataValid.BirthDayValid && !infoDataValid.EmailValid && 
        !infoDataValid.PasswordValid && !infoDataValid.AddressValid){
                  const _formData = new FormData();
                  _formData.append('Name',infoData.Name);
                  _formData.append('BirthDay',infoData.BirthDay);
                  _formData.append('Age',infoData.Age);
                  _formData.append('Address',infoData.Address);
                  _formData.append('Email',infoData.Email);
                  _formData.append('Password',infoData.Password);
                  _formData.append('Avatar',infoData.Avatar);
                 
                  callApi(`users/register`,'POST',_formData).then((item)=>{
                   console.log(item)
                     if(item.data>0){
                       setSuccess(true);
                     }else if(item.data===-1){
                       setError(false);
                       setMsgError("This email already exists");
                     }else{
                       setError(false);
                       setMsgError("Error login");
                     }

                 }); 
      }
      else{
        setError(false);
        
      }
     

  };
  if(success){
    return <Redirect to={{pathname:"/login"}} />
  }
  return (
    <div className={classes.root}>
      <Grid container spacing={3}>
        <Grid item xs={12}>
          <Paper className={classes.paper}>
            <ButtonBase className={classes.image}>
              <img className={classes.img} alt="complex" src="logo-top-new.svg" />
            </ButtonBase>
            <Typography className={classes.title} variant="h4">
                        Register Hoanguyen IT
            </Typography>
           
            <Grid item xs={12} sm container>
               
                <Grid item xs={6}>
                    <Typography gutterBottom variant="subtitle1">
                        Name
                    </Typography>
                    <TextField id="Name" name="Name" label="Name" variant="outlined" className={classes.txtInput} size="small"  onChange={handleChange}/>
                    {
                      infoDataValid.NameValid!=null && infoDataValid.NameValid  &&
                        <div className={classes.AlertError}>Please enter name!</div>
                    }
                </Grid>
                <Grid item xs={6}>
                    <Typography gutterBottom variant="subtitle1">
                        Age
                    </Typography>
                    <TextField id="Age" name="Age" label="Age" variant="outlined" className={classes.txtInput} size="small"  onChange={handleChange}/>
                    {
                      infoDataValid.AgeValid!=null && infoDataValid.AgeValid &&
                        <div className={classes.AlertError}>Please enter age your!</div>
                    }
                </Grid>
                <Grid item xs={6}>
                    <Typography gutterBottom variant="subtitle1">
                        Birth Day
                    </Typography>
                    <TextField id="BirthDay"  type="date" name="BirthDay" variant="outlined" className={classes.txtInput} size="small"  onChange={handleChange}/>
                    {
                      infoDataValid.BirthDayValid!=null && infoDataValid.BirthDayValid &&
                        <div className={classes.AlertError}>Please enter BirthDay your!</div>
                    }
                </Grid>
                <Grid item xs={6}>
                    <Typography gutterBottom variant="subtitle1">
                        Email
                    </Typography>
                    <TextField id="Email"  type="email" name="Email" label="Email" variant="outlined" className={classes.txtInput} size="small"  onChange={handleChange}/>
                    {
                      infoDataValid.EmailValid!=null && infoDataValid.EmailValid &&
                        <div className={classes.AlertError}>Please enter Email your!</div>
                    }
                </Grid>
                
                <Grid item xs={6}>
                    <Typography gutterBottom variant="subtitle1">
                        Password
                    </Typography>
                    <TextField id="Password" type="password" name="Password" label="Password" variant="outlined" className={classes.txtInput} size="small"  onChange={handleChange}/>
                    {
                      infoDataValid.PasswordValid!=null && infoDataValid.PasswordValid &&
                        <div className={classes.AlertError}>Please enter Password your!</div>
                    }
                </Grid>
                <Grid item xs={6}>
                    <Typography gutterBottom variant="subtitle1">
                        Address
                    </Typography>
                    <TextField id="Address" name="Address" label="Address" variant="outlined" className={classes.txtInput} size="small"  onChange={handleChange}/>
                    {
                      infoDataValid.AddressValid!=null && infoDataValid.AddressValid &&
                        <div className={classes.AlertError}>Please enter Address your!</div>
                    }
                </Grid>
                <Grid item xs={6}>
                    <Typography gutterBottom variant="subtitle1">
                        Avatar
                    </Typography>
                    <input color="primary" accept="image/*" type="file" name="Avatar" id="icon-button-file"  onChange={handleChange}/>
                    {
                      infoDataValid.AvatarValid!=null && infoDataValid.AvatarValid &&
                        <div className={classes.AlertError}>Please enter Avatar your!</div>
                    }
                </Grid>
                <Grid item xs={12}>
                
                  <Button type="button" onClick={btnRegister} fullWidth variant="contained" color="primary" className={classes.submit} >
                    Sign In
                  </Button>
                  
                </Grid>
            </Grid>
            {!error && <Alert severity="error">{msgError}</Alert>}
           
            
          </Paper>
        </Grid>
      </Grid>
    </div>
  )
}

Trong đoạn code Register.js trên mình có sử dụng một số cấu hình như sau:
+ Thiếp lập các giá trị ban đầu cho các thẻ TextField, các bạn có dùng useState thay đổi cho từng field 
  VD như: 

const [name,setName] = useState(null)
  changeName = (e)=>{
      setName(e.target.value)
  }
  <input type="text" onChange={changeName} />

Các bạn có thể dùng cách trên cho từng field trong Form, tại mình làm biến vì set nhiều quá nhức đầu lắm, nên mình tìm cách chơi kiểu bala bala này thử

 /*SET attribute in Form*/
  const ConfigData = Object.freeze({
    Name:"",
    Avatar:"",
    Age:0,
    Password: "",
    Email:"",
    Address:"",
    BirthDay:""
  });

  /*SET Valid attribute*/
  const ConfigDataValid = Object.freeze({
    NameValid:null,
    AvatarValid:null,
    AgeValid:null,
    PasswordValid: null,
    EmailValid:null,
    AddressValid:null,
    BirthDayValid:null
  })
  const [infoData, updateInfoData] = React.useState(ConfigData);
  const [infoDataValid, updateInfoDataValid] = React.useState(ConfigDataValid);
  const handleChange = (e) => {
       checkDataValid(e);
  };
  const checkDataValid=(e)=>{
    if(e.target.value.trim()===""){
      updateInfoDataValid({...infoDataValid,[e.target.name+"Valid"]:true})
      
    }
    else{
        updateInfoDataValid({...infoDataValid,[e.target.name+"Valid"]:false})
        if(e.target.name!=="Avatar"){
          updateInfoData({
            ...infoData,
            [e.target.name]:e.target.value.trim(), 
          });
        }else{
          updateInfoData({
            ...infoData,
            [e.target.name]:e.target.files[0] , 
          });
        }
    }
  }

Xử lý btnRegister , cũng giống như cách xử lý bên form Login vậy thôi, nhưng ở đây khác ở chổ mình phải append các data đến FormData sau đó gửi sang ASP Core để thêm, bởi vì bạn có thuộc tính thêm hình ảnh, nên mình cần dùng FormData nhé, còn nếu đăng ký thành viên của bạn không có thêm hình ảnh thì khỏi FormData cũng được 

const btnRegister = (e) => {
    e.preventDefault();
      if(!infoDataValid.NameValid && !infoDataValid.AgeValid && 
        !infoDataValid.BirthDayValid && !infoDataValid.EmailValid && 
        !infoDataValid.PasswordValid && !infoDataValid.AddressValid){
                  const _formData = new FormData();
                  _formData.append('Name',infoData.Name);
                  _formData.append('BirthDay',infoData.BirthDay);
                  _formData.append('Age',infoData.Age);
                  _formData.append('Address',infoData.Address);
                  _formData.append('Email',infoData.Email);
                  _formData.append('Password',infoData.Password);
                  _formData.append('Avatar',infoData.Avatar);
                 
                  callApi(`users/register`,'POST',_formData).then((item)=>{
                   console.log(item)
                     if(item.data>0){
                       setSuccess(true);
                     }else if(item.data===-1){
                       setError(false);
                       setMsgError("This email already exists");
                     }else{
                       setError(false);
                       setMsgError("Error login");
                     }

                 }); 
      }
      else{
        setError(false);
        
      }
     
  };
  if(success){
    return <Redirect to={{pathname:"/login"}} />
  }

+ ClientApp/components/MenuTop.js : Menu thì ta chỉ cấu hình sơ sơ nhìn ổn là dc, ở đây mình chưa hoàn thiện lắm, các bạn có gì làm thêm vào

import React,{useState} from 'react';
import {Link} from 'react-router-dom'
import { makeStyles } from '@material-ui/core/styles';
import {AppBar,Toolbar,Typography,IconButton} from '@material-ui/core';
import MenuIcon from '@material-ui/icons/Menu';
import MenuItem from '@material-ui/core/MenuItem';
import Menu from '@material-ui/core/Menu';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import { Redirect } from 'react-router-dom';
const useStyles = makeStyles((theme) => ({
    root: {
       width:'100%',
       flexGrow: 1,
    },
    title:{
        flexGrow:1
    },
    linkTo:{
      textDecoration:'none',
      color:'#000'
    },
    linkHome:{
      textDecoration:'none',
      color:'#fff'
    }
  }));
export default function MenuTop() {
  const classes = useStyles();
  const [anchorEl, setAnchorEl] = React.useState(null);
  const isMenuOpen = Boolean(anchorEl);
  const [logout,setLogout] = useState(false)
  const handleProfileMenuOpen = (event) => {
    setAnchorEl(event.currentTarget);
  };
  const handleMenuClose = () => {
    setAnchorEl(null);
  };
  const btnLogout =(e)=>{
    e.preventDefault();
    sessionStorage.clear();//clear all session
    setLogout(true);
    
  }
  const menuId = 'primary-search-account-menu';
  const renderMenu = (
    <Menu
      anchorEl={anchorEl}
      anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
      id={menuId}
      keepMounted
      transformOrigin={{ vertical: 'top', horizontal: 'right' }}
      open={isMenuOpen}
      onClose={handleMenuClose}
    >
      <MenuItem onClick={handleMenuClose}><Link to="/login" className={classes.linkTo}>Login</Link></MenuItem>
      <MenuItem onClick={handleMenuClose}><Link to="/register" className={classes.linkTo}>Register</Link></MenuItem>
      <MenuItem onClick={handleMenuClose}><Link to="/" className={classes.linkTo} onClick={btnLogout} >logout</Link></MenuItem>
    </Menu>
  );
    

    /*Check click logout */
    if(logout){
      return <Redirect to={{pathname:"/login"}} />
    }
    return (
        <div className={classes.root}>
            <AppBar position="static" color="primary">
              <Toolbar>
                <IconButton edge="start" color="inherit" aria-label="menu">
                    <MenuIcon />
                </IconButton>
                <Typography variant="h6" className={classes.title}>
                     <Link to="/" className={classes.linkHome}>Demo React ASP Core</Link>
                </Typography>
                <IconButton edge="end" color="inherit" aria-label="MoreVert" aria-controls={menuId}
                 aria-haspopup="true"
              onClick={handleProfileMenuOpen}>
                    <MoreVertIcon />
                </IconButton>
              </Toolbar>
            </AppBar>
            {renderMenu}
        </div>
    )
}

Okay vậy là xong, giờ ta cần chỉnh sửa ClientApp/App.js như sau:

import React, { Component } from 'react';
import { Route } from 'react-router';
import Container from '@material-ui/core/Container';
import CssBaseline from '@material-ui/core/CssBaseline';
import MenuTop from './components/MenuTop';
import Home from './components/Home';
import Register from './components/Register';
import Login from './components/Login';
export default class App extends Component {
  displayName = App.name;
  render() {
    return (
       <React.Fragment>
        <CssBaseline />
        <MenuTop />
        <Container maxWidth="sm">
           <Route exact path='/' component={Login} />
           <Route path='/home' component={Home} />
           <Route path='/login' component={Login} />
           <Route path='/register' component={Register} />
        </Container>
    </React.Fragment>
    );
  }
}

Tiếp tục chỉnh sửa file ClientApp/index.js 

//import 'bootstrap/dist/css/bootstrap.css';
//import 'bootstrap/dist/css/bootstrap-theme.css';
import './index.css';
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
const rootElement = document.getElementById('root');

ReactDOM.render(
  <BrowserRouter basename={baseUrl}>
    <App />
  </BrowserRouter>,
  rootElement);

registerServiceWorker();

Okay, vậy là xong(Backend & FrontEnd) giờ chúng ta chỉ chạy lên thôi, ở đây có 2 cách chạy
1. Bạn chạy cùng port với asp core (có nghĩa là, chỉ cần run project asp lên thôi là được, nó sẽ tự chạy react luôn) cùng 1 port sẽ không bị lỗi Cors
2. Bạn muốn React chạy port khác để request tới ASP Core thì bạn run 2 cái luôn, để run react( npm start), còn ASP thì chỉ bấm start server trong visual studio 2019 là được, sẽ xảy ra lỗi Cors, để hết lỗi này bạn cần cấu hình Cors trong file Startup.cs của ASP 

Demo:

Create Register & Login using ASP Core 2.1 + React (part 2)

Create Register & Login using ASP Core 2.1 + React (part 2)

Create Register & Login using ASP Core 2.1 + React (part 2)


 

Tag: React
x

Ủng hộ tôi bằng cách click vào quảng cáo. Để tôi có kinh phí tiếp tục phát triển Website!(Support me by clicking on the ad. Let me have the money to continue developing the Website!)