First React Application

girl, stairs, heaven-5824777.jpg

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
Create a service component to call the backend service, and return the result in Async fashion.
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
      );
}
error: Content is protected !!
Scroll to Top