DEV Community

Levi Ackerman
Levi Ackerman

Posted on • Edited on

Vite react axios frontend

New terminal at highest level
Npm create vite@latest
Install package
Project name : frontend
React framework
Javascript +swc
Cd frontend
Npm install
npm run dev

This will load the default site

Then install the dependencies we will use

npm install @emotion/react @emotion/styled @hookform/resolvers @mui/icons-material @mui/material axios react react-dom react-hook-form react-router-dom yup


Enter fullscreen mode Exit fullscreen mode
npm install -D @types/react @types/react-dom @vitejs/plugin-react-swc eslint eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-react-refresh vite

Enter fullscreen mode Exit fullscreen mode

You can add tailwind css if you like

Clean up index.css in src so it looks like this


:root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
}


html, body{
  height: 100%;
  margin: 0;
  padding: 0;
}


Enter fullscreen mode Exit fullscreen mode

Main.jsx should look like this

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import {BrowserRouter as Router} from 'react-router-dom'


ReactDOM.createRoot(document.getElementById('root')).render(
  <Router>
      <React.StrictMode>
         <App />
     </React.StrictMode>
  </Router>


)
Enter fullscreen mode Exit fullscreen mode

App.jsx should look like this

import { useState } from 'react'
import './App.css'
import Home from './components/Home'
import Register from './components/Register'
import Login from './components/Login'
import About from './components/About'
import Navbar from './components/Navbar'
import {Routes, Route, useLocation} from 'react-router-dom'
import ProtectedRoute from './components/ProtectedRoutes'
import PasswordResetRequest from './components/PasswordResetRequest'
import PasswordReset from './components/PasswordReset'


function App() {
  const location = useLocation()
  const noNavbar = location.pathname === "/register" || location.pathname === "/" || location.pathname.includes("password")


  return (
    <>
      {
        noNavbar ?
        <Routes>
            <Route path="/" element={<Login/>}/>
            <Route path="/register" element={<Register/>}/>
            <Route path="/request/password_reset" element={<PasswordResetRequest/>}/>
            <Route path="/password-reset/:token" element={<PasswordReset/>}/>
        </Routes>


        :


        <Navbar
        content={
          <Routes>
            <Route element={<ProtectedRoute/>}>
                <Route path="/home" element={<Home/>}/>
                <Route path="/about" element={<About/>}/>
            </Route>
          </Routes>


        }
      />
      }
    </>
  )
}


export default App
Enter fullscreen mode Exit fullscreen mode

If your not using tailwind at the moment app.css could look like this

.myBackground{
  width:100vw;
  height: 100vh;
  display: flex;
  align-items: center;


  justify-content: center;
  background: rgb(205,0,118);
  background: linear-gradient(137deg, rgba(205,0,118,1) 0%, rgba(85,35,185,1) 68%);
}


.whiteBox{
  background-color: white;
  padding: 20px;
  min-width: 300px;
  height: 70%;
  width: 25%;
  border: 1px solid #333;
}


.itemBox{
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 80px;
}


.title{
  font-size: 30px;
  color: #333;
}


.myForm,
.myButton{
  width: 100%;
}


.myButton{
  background: rgb(66, 8, 160) !important;
}

Enter fullscreen mode Exit fullscreen mode

Also make sure your .eslintrc.cjs looks like this

module.exports = {
  root: true,
  env: { browser: true, es2020: true },
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:react/jsx-runtime',
    'plugin:react-hooks/recommended',
  ],
  ignorePatterns: ['dist', '.eslintrc.cjs'],
  parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
  settings: { react: { version: '18.2' } },
  plugins: ['react-refresh'],
  rules: {
    'react-refresh/only-export-components': [
      'warn',
      { allowConstantExport: true },
    ],
  },
}

Enter fullscreen mode Exit fullscreen mode

Then in your src folder create a new folder components

We are going to create our pages first

About.jsx

const About = () =>{
    return(
        <div>
            This is the about page
        </div>
    )


}


export default About
Enter fullscreen mode Exit fullscreen mode

Home.jsx

import AxiosInstance from './AxiosInstance'
import {React, useEffect, useMemo, useState} from 'react'
import {Box} from '@mui/material'


const Home = () =>{


    const [myData, setMyData] = useState()
    const [loading,setLoading] = useState(true)


    const GetData = () => {
        AxiosInstance.get(`users/`).then((res) =>{
            setMyData(res.data)
            console.log(res.data)
            setLoading(false)
        })
    }


    useEffect(() =>{
        GetData();
    },[])


    return(
        <div>
            { loading ? <p>Loading data...</p> :
            <div>
                {myData.map((item, index) => (
                    <Box key={index} sx={{p:2, m:2, boxShadow:3}}>
                        <div> ID: {item.id}</div>
                        <div> Email: {item.email} </div>
                    </Box>
                )
                )}


            </div>


            }
        </div>
    )


}


export default Home

Enter fullscreen mode Exit fullscreen mode

Login.jsx

import '../App.css'
import {React, useState} from 'react'
import { Box } from '@mui/material'
import MyTextField from './forms/MyTextField'
import MyPassField from './forms/MyPassField'
import MyButton from './forms/MyButton'
import {Link} from 'react-router-dom'
import {useForm} from 'react-hook-form'
import AxiosInstance from './AxiosInstance'
import { useNavigate } from 'react-router-dom'
import MyMessage from './Message'


const Login = () =>{
    const navigate = useNavigate()
    const {handleSubmit, control} = useForm()
    const [ShowMessage, setShowMessage] = useState(false)


    const submission = (data) => {
        AxiosInstance.post(`login/`,{
            email: data.email,
            password: data.password,
        })


        .then((response) => {
            console.log(response)
            localStorage.setItem('Token', response.data.token)
            navigate(`/home`)
        })
        .catch((error) => {
            setShowMessage(true)
            console.error('Error during login', error)
        })
    }



    return(
        <div className={"myBackground"}>
            {ShowMessage ? <MyMessage text={"Login has failed, please try again, or reset your password"} color={'#EC5A76'}/> : null}
            <form onSubmit={handleSubmit(submission)}>
            <Box className={"whiteBox"}>


                <Box className={"itemBox"}>
                    <Box className={"title"}> Login for Auth App </Box>
                </Box>


                <Box className={"itemBox"}>
                    <MyTextField
                    label={"Email"}
                    name ={"email"}
                    control={control}
                    />
                </Box>


                <Box className={"itemBox"}>
                    <MyPassField
                    label={"Password"}
                    name ={"password"}
                    control={control}
                    />
                </Box>


                <Box className={"itemBox"}>
                    <MyButton
                        label={"Login"}
                        type={"submit"}
                    />
                </Box>


                <Box className={"itemBox"} sx={{flexDirection:'column'}}>
                    <Link to="/register"> No account yet? Please register! </Link>
                    <Link to="/request/password_reset"> Password forgotten? Click here </Link>
                </Box>




            </Box>


        </form>

        </div>
    )


}


export default Login
Enter fullscreen mode Exit fullscreen mode

Register.jsx

import '../App.css'
import { Box } from '@mui/material'
import MyTextField from './forms/MyTextField'
import MyPassField from './forms/MyPassField'
import MyButton from './forms/MyButton'
import {Link} from 'react-router-dom'
import {useForm} from 'react-hook-form'
import AxiosInstance from './AxiosInstance'
import { useNavigate } from 'react-router-dom'
import {yupResolver} from "@hookform/resolvers/yup"
import * as yup from "yup"




const Register = () =>{
    const navigate = useNavigate()


    const schema = yup
    .object({
        email: yup.string().email('Field expects an email adress').required('Email is a required field'),
        password: yup.string()
                    .required('Password is a required field')
                    .min(8,'Password must be at least 8 characters')
                    .matches(/[A-Z]/,'Password must contain at least one uppercase letter')
                    .matches(/[a-z]/,'Password must contain at least one lower case letter')
                    .matches(/[0-9]/,'Password must contain at least one number')
                    .matches(/[!@#$%^&*(),.?":;{}|<>+]/, 'Password must contain at least one special character'),
        password2: yup.string().required('Password confirmation is a required field')
                     .oneOf([yup.ref('password'),null], 'Passwords must match')


    })  


    const {handleSubmit, control} = useForm({resolver: yupResolver(schema)})


    const submission = (data) => {
        AxiosInstance.post(`register/`,{
            email: data.email,
            password: data.password,
        })


        .then(() => {
            navigate(`/`)
        }
        )
    }


    return(
        <div className={"myBackground"}>


            <form onSubmit={handleSubmit(submission)}>


            <Box className={"whiteBox"}>


                <Box className={"itemBox"}>
                    <Box className={"title"}> User registration </Box>
                </Box>


                <Box className={"itemBox"}>
                    <MyTextField
                    label={"Email"}
                    name ={"email"}
                    control={control}
                    />
                </Box>


                <Box className={"itemBox"}>
                    <MyPassField
                    label={"Password"}
                    name={"password"}
                    control={control}
                    />
                </Box>


                <Box className={"itemBox"}>
                    <MyPassField
                    label={"Confirm password"}
                    name={"password2"}
                    control={control}
                    />
                </Box>


                <Box className={"itemBox"}>
                    <MyButton
                        type={"submit"}
                        label={"Register"}
                    />
                </Box>


                <Box className={"itemBox"}>
                    <Link to="/"> Already registered? Please login! </Link>
                </Box>




            </Box>


            </form>

        </div>
    )


}


export default Register
Enter fullscreen mode Exit fullscreen mode

Now were going to make our page components

Message.jsx

import { Box } from "@mui/material"




const MyMessage = ({text, color}) =>{
    return(
        <Box sx={{
            backgroundColor: color,
            color:'#FFFFFF',
            width: '90%',
            height: '40px',
            position:'absolute',
            top:'20px',
            display:'flex',
            justifyContent:'center',
            alignItems:'center'
            }}>
            {text}
        </Box>
    )
}


export default MyMessage
Enter fullscreen mode Exit fullscreen mode

Navbar.jsx


import * as React from 'react';
import Box from '@mui/material/Box';
import Drawer from '@mui/material/Drawer';
import AppBar from '@mui/material/AppBar';
import CssBaseline from '@mui/material/CssBaseline';
import Toolbar from '@mui/material/Toolbar';
import List from '@mui/material/List';
import Typography from '@mui/material/Typography';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import InboxIcon from '@mui/icons-material/MoveToInbox';
import HomeIcon from '@mui/icons-material/Home';
import InfoIcon from '@mui/icons-material/Info';
import {Link, useLocation} from 'react-router-dom'
import LogoutIcon from '@mui/icons-material/Logout';
import AxiosInstance from './AxiosInstance';
import { useNavigate } from 'react-router-dom';


const drawerWidth = 240;


export default function Navbar(props) {
  const {content} = props
  const location = useLocation()
  const path = location.pathname
  const navigate = useNavigate()


  const logoutUser = () =>{
     AxiosInstance.post(`logoutall/`,{
     })
     .then( () => {
        localStorage.removeItem("Token")
        navigate('/')
     }


     )
  }


  return (
    <Box sx={{ display: 'flex' }}>
      <CssBaseline />
      <AppBar position="fixed" sx={{ zIndex: (theme) => theme.zIndex.drawer + 1 }}>
        <Toolbar>
          <Typography variant="h6" noWrap component="div">
            Clipped drawer
          </Typography>
        </Toolbar>
      </AppBar>
      <Drawer
        variant="permanent"
        sx={{
          width: drawerWidth,
          flexShrink: 0,
          [`& .MuiDrawer-paper`]: { width: drawerWidth, boxSizing: 'border-box' },
        }}
      >
        <Toolbar />
        <Box sx={{ overflow: 'auto' }}>
          <List>

              <ListItem key={1} disablePadding>
                <ListItemButton component={Link} to="/home" selected={"/home" === path}>
                  <ListItemIcon>
                        <HomeIcon />
                  </ListItemIcon>
                  <ListItemText primary={"Home"} />
                </ListItemButton>
              </ListItem>


              <ListItem key={2} disablePadding>
              <ListItemButton component={Link} to="/about" selected={"/about" === path}>
                  <ListItemIcon>
                        <InfoIcon />
                  </ListItemIcon>
                  <ListItemText primary={"About"} />
                </ListItemButton>
              </ListItem>


              <ListItem key={3} disablePadding>
              <ListItemButton onClick={logoutUser}>
                  <ListItemIcon>
                        <LogoutIcon/>
                  </ListItemIcon>
                  <ListItemText primary={"Logout"} />
                </ListItemButton>
              </ListItem>

          </List>

        </Box>
      </Drawer>
      <Box component="main" sx={{ flexGrow: 1, p: 3 }}>
        <Toolbar />
            {content}
      </Box>
    </Box>
  );
}
Enter fullscreen mode Exit fullscreen mode

Most importantly is our axios instance that connects our frontend and backend

import axios from 'axios'


const baseUrl = 'http://127.0.0.1:8000/'


const AxiosInstance = axios.create({
    baseURL: baseUrl,
    timeout: 5000,
    headers:{
        "Content-Type":"application/json",
         accept: "application/json"
    }
})


AxiosInstance.interceptors.request.use(
    (config) => {
        const token = localStorage.getItem('Token')
        if(token){
            config.headers.Authorization = `Token ${token}`
        }
        else{
            config.headers.Authorization = ``
        }
        return config;
    }
)


AxiosInstance.interceptors.response.use(
    (response) => {
        return response
    },
    (error) => {
        if(error.response && error.response.status === 401){
            localStorage.removeItem('Token')
        }


    }
)


export default AxiosInstance;
Enter fullscreen mode Exit fullscreen mode

Then our PasswordReset page

import '../App.css'
import {React, useState} from 'react'
import { Box } from '@mui/material'
import MyTextField from './forms/MyTextField'
import MyPassField from './forms/MyPassField'
import MyButton from './forms/MyButton'
import {useParams } from 'react-router-dom'
import {useForm} from 'react-hook-form'
import AxiosInstance from './AxiosInstance'
import { useNavigate } from 'react-router-dom'
import MyMessage from './Message'


const PasswordReset = () =>{
    const navigate = useNavigate()
    const {handleSubmit, control} = useForm()
    const {token} = useParams()
    console.log(token)
    const [ShowMessage, setShowMessage] = useState(false)




    const submission = (data) => {
        AxiosInstance.post(`api/password_reset/confirm/`,{
            password: data.password,
            token: token,
        })


        .then((response) => {
            setShowMessage(true)
            setTimeout(() =>{
                navigate('/')
            }, 6000 )
        })

    }
    return(
        <div className={"myBackground"}>


        {ShowMessage ? <MyMessage text={"Your password reset was successfull, you will be directed to the login page in a second"} color={'#69C9AB'}/> : null}
        <form onSubmit={handleSubmit(submission)}>





        <Box className={"whiteBox"}>


            <Box className={"itemBox"}>
                <Box className={"title"}> Reset password </Box>
            </Box>


            <Box className={"itemBox"}>
                <MyPassField
                        label={"Password"}
                        name ={"password"}
                        control={control}
                        />
            </Box>


            <Box className={"itemBox"}>
                <MyPassField
                        label={"Confirm password"}
                        name ={"password2"}
                        control={control}
                        />
            </Box>


            <Box className={"itemBox"}>
                <MyButton
                    label={"Reset password"}
                    type={"submit"}
                />
            </Box>


            <Box className={"itemBox"} sx={{flexDirection:'column'}}>

            </Box>




        </Box>


    </form>

    </div>
    )


}


export default PasswordReset
Enter fullscreen mode Exit fullscreen mode
Password reset request 

import '../App.css'
import {React, useState} from 'react'
import { Box } from '@mui/material'
import MyTextField from './forms/MyTextField'
import MyPassField from './forms/MyPassField'
import MyButton from './forms/MyButton'
import {Link} from 'react-router-dom'
import {useForm} from 'react-hook-form'
import AxiosInstance from './AxiosInstance'
import { useNavigate } from 'react-router-dom'
import MyMessage from './Message'


const PasswordResetRequest = () =>{
    const navigate = useNavigate()
    const {handleSubmit, control} = useForm()


    const [ShowMessage, setShowMessage] = useState(false)




    const submission = (data) => {
        AxiosInstance.post(`api/password_reset/`,{
            email: data.email,
        })


        .then((response) => {
            setShowMessage(true)
        })

    }
    return(
        <div className={"myBackground"}>


        {ShowMessage ? <MyMessage text={"If your email exists you have received an email with instructions for resetting the password"}  color={'#69C9AB'}/> : null}
        <form onSubmit={handleSubmit(submission)}>





        <Box className={"whiteBox"}>


            <Box className={"itemBox"}>
                <Box className={"title"}> Request password reset </Box>
            </Box>


            <Box className={"itemBox"}>
                <MyTextField
                label={"Email"}
                name ={"email"}
                control={control}
                />
            </Box>


            <Box className={"itemBox"}>
                <MyButton
                    label={"Request password reset"}
                    type={"submit"}
                />
            </Box>


            <Box className={"itemBox"} sx={{flexDirection:'column'}}>

            </Box>




        </Box>


    </form>

    </div>
    )


}


export default PasswordResetRequest
Enter fullscreen mode Exit fullscreen mode

Protected routes

import {Outlet, Navigate} from 'react-router-dom'


const ProtectedRoute = () => {
    const token = localStorage.getItem('Token')


    return(


        token ? <Outlet/> : <Navigate to="/" />
    )


}


export default ProtectedRoute

Enter fullscreen mode Exit fullscreen mode

In the components folder create a new folder called forms

Create a file called MyButton.jsx

import * as React from 'react';
import Button from '@mui/material/Button';


export default function MyButton(props) {
  const {label,type} = props
  return (
      <Button type={type} variant="contained" className={"myButton"}>
            {label}
      </Button>
Enter fullscreen mode Exit fullscreen mode

MyPassField.jsx

import * as React from 'react';
import IconButton from '@mui/material/IconButton';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import InputAdornment from '@mui/material/InputAdornment';
import FormControl from '@mui/material/FormControl';
import Visibility from '@mui/icons-material/Visibility';
import VisibilityOff from '@mui/icons-material/VisibilityOff';
import { FormHelperText } from '@mui/material';
import {Controller} from 'react-hook-form'


export default function MyPassField(props) {
  const [showPassword, setShowPassword] = React.useState(false);
  const {label,name, control} = props


  const handleClickShowPassword = () => setShowPassword((show) => !show);


  const handleMouseDownPassword = (event) => {
    event.preventDefault();
  };


  return (
    <Controller
        name = {name}
        control = {control}
        render = {({
            field:{onChange, value},
            fieldState : {error},
            formState,
        }) =>(


          <FormControl variant="outlined" className={"myForm"}>
          <InputLabel htmlFor="outlined-adornment-password">{label}</InputLabel>
          <OutlinedInput
            id="outlined-adornment-password"
            onChange={onChange}
            value={value}
            error = {!!error}
            type={showPassword ? 'text' : 'password'}
            endAdornment={
              <InputAdornment position="end">
                <IconButton
                  aria-label="toggle password visibility"
                  onClick={handleClickShowPassword}
                  onMouseDown={handleMouseDownPassword}
                  edge="end"
                >
                  {showPassword ? <VisibilityOff /> : <Visibility />}
                </IconButton>
              </InputAdornment>
            }
            label={label}
          />


        <FormHelperText sx={{color:"#d32f2f"}}> {error?.message} </FormHelperText>


        </FormControl>



    )
  }

 />



  );
}
Enter fullscreen mode Exit fullscreen mode

MyTextField.jsx

import * as React from 'react';
import '../../App.css'
import TextField from '@mui/material/TextField';
import {Controller} from 'react-hook-form'


export default function MyTextField(props) {
  const {label, name, control} = props
  return (


     <Controller
        name = {name}
        control = {control}
        render = {({
            field:{onChange, value},
            fieldState : {error},
            formState,
        }) =>(


          <TextField
          id="outlined-basic"
          onChange = {onChange}
          value = {value}
          label= {label}
          variant="outlined"
          className={"myForm"}
          error = {!!error}
          helperText = {error?.message}
           />

        )
      }

     />


  );
}
Enter fullscreen mode Exit fullscreen mode

And there you have it django react log in

To run it use python manage.py runserver on the backend and rpm run dev on frontend

);
}

MyPassField.jsx
import * as React from 'react';
import IconButton from '@mui/material/IconButton';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import InputAdornment from '@mui/material/InputAdornment';
import FormControl from '@mui/material/FormControl';
import Visibility from '@mui/icons-material/Visibility';
import VisibilityOff from '@mui/icons-material/VisibilityOff';
import { FormHelperText } from '@mui/material';
import {Controller} from 'react-hook-form'

export default function MyPassField(props) {
const [showPassword, setShowPassword] = React.useState(false);
const {label,name, control} = props

const handleClickShowPassword = () => setShowPassword((show) => !show);

const handleMouseDownPassword = (event) => {
event.preventDefault();
};

return (
name = {name}
control = {control}
render = {({
field:{onChange, value},
fieldState : {error},
formState,
}) =>(

      <FormControl variant="outlined" className={"myForm"}>
      <InputLabel htmlFor="outlined-adornment-password">{label}</InputLabel>
      <OutlinedInput
        id="outlined-adornment-password"
        onChange={onChange}
        value={value}
        error = {!!error}
        type={showPassword ? 'text' : 'password'}
        endAdornment={
          <InputAdornment position="end">
            <IconButton
              aria-label="toggle password visibility"
              onClick={handleClickShowPassword}
              onMouseDown={handleMouseDownPassword}
              edge="end"
            >
              {showPassword ? <VisibilityOff /> : <Visibility />}
            </IconButton>
          </InputAdornment>
        }
        label={label}
      />


    <FormHelperText sx={{color:"#d32f2f"}}> {error?.message} </FormHelperText>


    </FormControl>



)
Enter fullscreen mode Exit fullscreen mode

}

/>

);
}

MyTextField.jsx

import * as React from 'react';
import '../../App.css'
import TextField from '@mui/material/TextField';
import {Controller} from 'react-hook-form'

export default function MyTextField(props) {
const {label, name, control} = props
return (

 <Controller
    name = {name}
    control = {control}
    render = {({
        field:{onChange, value},
        fieldState : {error},
        formState,
    }) =>(


      <TextField
      id="outlined-basic"
      onChange = {onChange}
      value = {value}
      label= {label}
      variant="outlined"
      className={"myForm"}
      error = {!!error}
      helperText = {error?.message}
       />

    )
  }

 />
Enter fullscreen mode Exit fullscreen mode

);
}

And there you have it django react log in

To run it use python manage.py runserver on the backend and rpm run dev on frontend

Top comments (0)