Introduction
Once We have covered some of the basic stuff about React App, we will be next looking at setting up an application and applying those concepts into an Application.
We will be creating the following features for our APP.
- A functional Login Page with Toastify effects
- Session Storage
- Landing page with Icons
- Routing and UseNavigate
- Props for Inter Component communication
- UseState and Event Handling
- File Upload
- File Download
- Axios for Backend communication
Login Page
We will create a full-function login page, The Prerequisites for the login page are below
- npm add react-toastify
- npm add add axios
- npm add react-router-dom
We are using useState to get the userName and Password from UI and pass this to the backend server to verify whether UserDetails are correct. If the Passwords match then they are stored in sessionStorage, and also display the success beautifully using the toast library.
import { useState } from "react"
import colors from "./color"
import { toast } from "react-toastify"
import { login } from "../services/user"
import { useNavigate } from "react-router-dom"
const Login = () => {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const navigate = useNavigate()
const styles = {
container: {
width: '300px',
height: '200px',
margin: 'auto',
position: 'absolute',
top: '0',
left: '0',
right: '0',
buttom: '0',
},
inputContainer: {
margin: '20px',
},
input: {
borderRadius: '20px',
},
buttonSignin: {
width: '100%',
backgroundColor: colors.blue,
border: 'none',
height: '30px',
borderRadius: '20px',
color: 'white',
fontSize: '18px',
fontWeight: '800',
}
}
const onLogin = async () => {
if (email.length === 0) {
toast.warning('enter email')
} else if (password.length === 0) {
toast.warning('enter password')
} else {
const result = await login(email, password)
if (result['status'] === 'success') {
sessionStorage['token'] = result['token']
sessionStorage['username'] = result['username']
toast.success('welcome to the application')
navigate('/home')
} else {
toast.error('Login failed, please try again')
}
}
}
return (
<div style={styles.container}>
<h2 style={{textAlign: 'center', padding: '20px'}}>CLIENT LOGIN</h2>
<div className="form">
<div style={styles.inputContainer} className="form-group">
<input
onChange={(e) => setEmail(e.target.value)}
style={styles.input}
type="email"
className="form-control"
placeholder="email address"
/>
</div>
<div style={styles.inputContainer} className="form-group">
<input
onChange={(e) => setPassword(e.target.value)}
style={styles.input}
type="password"
className="form-control"
placeholder="password" />
</div>
<div style={styles.inputContainer} className="form-group">
<button onClick={onLogin} style={styles.buttonSignin}>Sign in</button>
</div>
</div>
</div>
)
}
export default Login
import axios from "axios"
import { createUrl } from "./utils"
export const login = async (email, password) => {
const response = await axios.
post(http://localhost:8081/login, {
email,
password
})
return response.data
}
Routing
We have added react-router in pre-requisites, to route requests to different pages, We will use App.js for building React Router.
import MenuItems from "./components/MenuItems";
import UploadFile from "./components/UploadFile";
import Lists from "./components/Lists";
import Login from "./pages/Login";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "./pages/Home";
import { ToastContainer, toast } from "react-toastify";
import 'react-toastify/dist/ReactToastify.css';
import CreateFolder from "./components/CreateFolder";
const AuthorizedUser = () => {
return sessionStorage['token'] ? <Home/>: <Login/>
}
function App() {
return (
<div className="container">
<BrowserRouter>
<Routes>
<Route path="/" element={<AuthorizedUser/>}/>
<Route path="/login" element={<Login/>}/>
<Route path="/home" element={<Home/>}/>
</Routes>
</BrowserRouter>
<ToastContainer/>
</div>
)
}
export default App;
Landing Page & Props
After successful login, we will route the request to the Home page, The Home page can be configured to display our business views. We will create a Home page to display our Business landing page. We will use
Create a MenuItems Component, which will iterate through each Value
import MenuItem from "./MenuItem"
const MenuItems = () => {
const menuItems = [
{
icon: 'repoInfo.png',
title: 'Profile',
route: 'profile'
},
{
icon: 'list.png',
title: 'List',
route: 'list'
},
{
icon: 'createFolder.png',
title: 'Add Product',
route: 'addProduct'
},
{
icon: 'deleteFolder.png',
title: 'Delete product',
route: 'deleteProduct'
},
{
icon: 'uploadFile.png',
title: 'Uploadproduct',
route: 'uploadProduct'
},
{
icon: 'downloadFile.png',
title: 'Downloadproduct',
route: 'downloadproduct'
}
]
return (
<div>
{menuItems.map((item, index) => {
return <MenuItem item={item} />
})}
</div>
)
}
export default MenuItems
Props
Refers to a technique for sharing code between React components using a prop whose value is a function. We are using MenuItems to Share code to Menuitem so as to display them in the next Component.
import { Link } from "react-router-dom"
const styles = {
title: {
color: 'rgb(15, 20, 25)',
fontSize: '20px',
marginLeft: '20px',
textDecoration: 'none',
},
container: {
paddingTop: '30px',
height: '50px',
},
}
const MenuItem = (props) => {
const { title, icon, route } = props.item
return (
<div style={styles.container}>
<Link to={"/" + route} style={{textDecoration: 'none'}}>
<img src={require('../assets/' + icon)}></img>
<span style={styles.title}>{title}</span>
</Link>
</div>
)
}
export default MenuItem
List Contents
We are using list to display folders, and files obtained from backend, we can similarly use this template to display Products or things needed by business.
import { Fragment, Suspense, useEffect, useState } from "react"
import { toast } from "react-toastify"
import colors from "../pages/color"
import { createFolder, listFolder } from "../services/Folder"
import CreateFolder from "./CreateFolder"
import FileDownload from "./FileDownload"
import FileUpload from "./FileUpload"
import List from "./List"
const Files = () => {
const [files, setFiles] = useState([])
const [isLoaded, setIsLoaded] = useState(false);
const [value, setValue] = useState('No');
const handleChange = (e) => {
setValue(e.target.value);
};
useEffect(() => {
(async () => {
getList()
})()
}, [])
const getList = async () => {
const result = await listFolder()
if (result['status'] === 'success') {
setFiles([result])
setIsLoaded(true);
} else {
toast.error("Cannot list the Folders")
setIsLoaded(false);
}
}
return (
<div className="container">
{!isLoaded && <p>loading...</p>}
<div className="row">
{
files.map(
(item, index) => {
return (
<Fragment key={index}>
<List item={item} />
</Fragment>
)
}
)
}
</div>
<br></br>
<h5 style={{ color: colors.blue }}>Choose from following </h5>
<select class="form-select" aria-label="Default select example" style={{ width: '300px', }}
value={value} onChange={handleChange}>
<option selected>Open this select menu</option>
<option value="FileUpoad">FileUpoad</option>
<option value="FileDownload">FileDownload</option>
<option value="CreateFolder">CreateFolder</option>
</select>
<p>{`You selected ${value}`}</p>
{value == 'FileUpoad' &&
<div className="row">
<FileUpload updateList={getList}/>
</div>
}
{value == 'FileDownload' &&
<div className="row">
<FileDownload/>
</div>
}
{value == 'CreateFolder' &&
<div className="row">
<CreateFolder updateList={getList}/>
</div>
}
</div>
)
}
export default Files
Iterate through the list values obtained from backend and display to Frontend
import colors from "../pages/color"
import "../index.css";
import { Fragment } from "react";
import { useNavigate } from "react-router-dom";
const styles = {
container: {
border: `solid ${colors.grey} 1px`,
padding: '20px',
marginTop: '10px',
},
hierarchy: {
fontFamily: 'FontAwesome',
width: '300px',
},
foldercontainer: {
display: 'block',
padding: '5px 5px 5px 10px',
},
filecontainer: {
display: 'block',
padding: '5px 5px 5px 10px',
},
folder: {
color: 'green',
cursor: 'pointer',
},
file: {
color: 'blue',
cursor: 'pointer',
},
createButton: {
width: '100px',
backgroundColor: '#1d9bf0',
color: 'white',
border: 'none',
borderRadius: '15px',
height: '25px',
marginTop: '10px',
fontSize: '14px',
boxShadow: 'rgb(0 0 0 / 8%) 0px 8px 28px;',
},
}
const List = (props) => {
const {
status,
folders,
files,
} = props.item
function MouseOver(event) {
event.target.style.background = 'yellow';
}
function MouseOut(event) {
event.target.style.background = "";
}
const navigate = useNavigate()
const onGoBack = () => {
navigate('/home');
}
return (
<div style={styles.container}>
<div className="row">
<div className="col-8">
<h4 style={{ textAlign: 'left', padding: '5px', color: colors.blue }}>Repository Contents: </h4>
</div>
<div className="col-4">
<button onClick={onGoBack} style={styles.createButton}>Back</button>
</div>
</div>
<div style={{ marginLeft: '20px' }} className="row">
<div style={styles.hierarchy}>
<div style={styles.foldercontainer}>
{folders.map((folder, index) => (
<Fragment key={index}>
<span onMouseOver={MouseOver} onMouseOut={MouseOut}
className="fa-folder-o listDisplay" style={styles.folder} data-isexpanded="true" >{folder}{"\n"}<br /></span>
</Fragment>
))}
</div>
</div>
<div style={styles.hierarchy}>
<div style={styles.filecontainer}>
{files.map((file, index) => (
<Fragment key={index}>
<span onMouseOver={MouseOver} onMouseOut={MouseOut}
className="fa-file-code-o listDisplay" style={styles.file} data-isexpanded="true" >{file}{"\n"}<br /></span>
</Fragment>
))}
</div>
</div>
</div>
</div>
)
}
export default List
Backend call
export const listFolder = async() => {
const response = await axios.get(createUrl('alfresco/listRootFolder'), {
})
return response.data
}
File Upload & Use State
We will do a file upload to upload a file to the Products list, also we are using useState to take inputs from Browser and Update the components on the page.
The React useState
Hook allows us to track state in a function component. It basically lets you turn your otherwise non-stateful/functional components into one that can have its own state.
import { useRef, useState } from "react"
import { toast } from "react-toastify"
import colors from "../pages/color"
import { createFolder, uploadFileToServer } from "../services/Folder"
const FileUpload = (props) => {
const styles = {
container: {
border: `solid ${colors.grey} 1px`,
height: '200px',
borderRadius: '5px',
padding: '20px',
marginTop: '10px',
},
header: {
fontSize: '20px',
fontWeight: '800',
},
input: {
margininTop: '30px',
border: '15px',
height: '20px',
// display: 'inline-block',
padding: '10px',
borderRadius: '20px',
},
button: {
padding: '10px',
marginRight: '10px',
},
createButton: {
width: '100px',
backgroundColor: '#1d9bf0',
color: 'white',
border: 'none',
borderRadius: '15px',
height: '25px',
marginTop: '10px',
fontSize: '14px',
boxShadow: 'rgb(0 0 0 / 8%) 0px 8px 28px',
}
}
const [file, setFile] = useState('');
const el = useRef();
const handleToUpdate = props.updateList;
const uploadFile = async () => {
const formData = new FormData();
formData.append("file", file);
console.log(' file from upload' + file)
const result = await uploadFileToServer(formData)
if (result['status'] === 'success') {
toast.success('Successfully uploaded file')
handleToUpdate()
} else {
toast.error('Could not upload file')
}
}
const handleChange = () => {
const uploadedfile = document.getElementById('upload_doc').files[0]
console.log(uploadedfile)
setFile(uploadedfile)
}
return (
<div style={styles.container}>
<div style={styles.header}>File Upload</div>
<br />
<div className="file-upload">
<input type="file" id='upload_doc' onChange={handleChange} />
<button onClick={uploadFile} className="upbutton">Upload File</button>
</div>
</div>
)
}
export default FileUpload
Backend call to Upload file
export const uploadFileToServer = async (formData) => {
const response = await axios.post(createUrl('uploadFile'), formData, {
headers: { "content-type": "multipart/form-data"}
})
return response.data
}
File Download & Axios
We will create a component for file download, with the help of Axios to call a backend service to download a file.
import { useState } from "react"
import { toast } from "react-toastify"
import colors from "../pages/color"
import { fileDownload } from "../services/Folder"
const FileDownload = () => {
const styles = {
container: {
border: `solid ${colors.grey} 1px`,
height: '200px',
borderRadius: '5px',
padding: '20px',
marginTop: '10px',
},
header: {
fontSize: '20px',
fontWeight: '800',
},
input: {
margininTop: '30px',
border: '15px',
height: '20px',
// display: 'inline-block',
padding: '10px',
borderRadius: '20px',
},
button: {
padding: '10px',
marginRight: '10px',
},
createButton: {
width: '100px',
backgroundColor: '#1d9bf0',
color: 'white',
border: 'none',
borderRadius: '15px',
height: '25px',
marginTop: '10px',
fontSize: '14px',
boxShadow: 'rgb(0 0 0 / 8%) 0px 8px 28px;',
},
}
const [fileName, setFileName] = useState('')
const onDownloadFile = () => {
if( fileName.length === 0) {
toast.warning('please enter file name')
} else {
toast.info('File Download Started')
const result = fileDownload(fileName)
}
}
return <div style={styles.container}>
<div style={styles.header}>Download File</div>
<br />
<input
onChange={(e) => setFileName(e.target.value)}
type="text"
style={styles.input}
className="form-control"
placeholder="Enter File to Download">
</input>
<br />
<div className='row'>
<div className='col'>
<div className='float'>
<button onClick={onDownloadFile} style={styles.createButton}>Download</button>
</div>
</div>
</div>
</div>
}
export default FileDownload
Backend service call
export const fileDownload = (fileName) => {
saveAs(
createUrl('downloadFile/' + fileName),
fileName
);
}