Learning the utilization of React Hooks and Reach Context with a small Employee management system.
A Pen by Anthony Hastings on CodePen.
<div id="app"></div> |
Learning the utilization of React Hooks and Reach Context with a small Employee management system.
A Pen by Anthony Hastings on CodePen.
console.clear(); | |
const { BrowserRouter, Link, Route, Switch, useHistory, useParams } = window.ReactRouterDOM; | |
const { createContext, Fragment, useContext, useEffect, useReducer, useState } = window.React; | |
const GlobalContext = createContext(); | |
const AppReducer = (state, action) => { | |
switch (action.type) { | |
case 'REMOVE_EMPLOYEE': { | |
return { | |
...state, | |
employees: state.employees.filter((employee) => { | |
return employee.id !== action.payload; | |
}) | |
}; | |
} | |
case 'ADD_EMPLOYEE': { | |
return { | |
...state, | |
employees: [...state.employees, action.payload] | |
}; | |
} | |
case 'EDIT_EMPLOYEE': { | |
return { | |
...state, | |
employees: state.employees.map((employee) => { | |
return (employee.id === action.payload.id) ? action.payload : employee; | |
}) | |
}; | |
} | |
default: { | |
return state; | |
} | |
} | |
}; | |
const GlobalState = ({ children }) => { | |
const [state, dispatch] = useReducer(AppReducer, { | |
employees: [ | |
{ id: uuidv4(), name: 'Bruce Wayne', location: 'Gotham', designation: 'Bachelor' } | |
] | |
}); | |
const addEmployee = (employee) => dispatch({ type: 'ADD_EMPLOYEE', payload: employee }); | |
const editEmployee = (employee) => dispatch({ type: 'EDIT_EMPLOYEE', payload: employee }); | |
const removeEmployee = (id) => dispatch({ type: 'REMOVE_EMPLOYEE', payload: id }); | |
return ( | |
<GlobalContext.Provider value={{ | |
employees: state.employees, | |
addEmployee, | |
editEmployee, | |
removeEmployee | |
}}> | |
{children} | |
</GlobalContext.Provider> | |
); | |
}; | |
const EmployeeList = () => { | |
const { employees, removeEmployee, editEmployee } = useContext(GlobalContext); | |
if (employees.length === 0) return null; | |
return ( | |
<Fragment> | |
{employees.map((employee) => ( | |
<div className="flex items-center bg-gray-100 mb-10 shadow" key={employee.id}> | |
<div className="flex-auto text-left px-4 py-2 m-2"> | |
<p className="text-gray-900 leading-none">{employee.name}</p> | |
<p className="text-gray-600">{employee.designation}</p> | |
<span className="inline-block text-sm font-semibold mt-1">{employee.location}</span> | |
</div> | |
<div className="flex-auto text-right px-4 py-2 m-2"> | |
<Link to={`/edit/${employee.id}`}> | |
<button title="Edit" onClick={() => editEmployee(employee.id)} className="bg-gray-300 hover:bg-gray-400 text-gray-800 font-semibold mr-3 py-2 px-4 rounded-full inline-flex items-center"> | |
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-edit"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg> | |
</button> | |
</Link> | |
<button title="Remove" onClick={() => removeEmployee(employee.id)} className="block bg-gray-300 hover:bg-gray-400 text-gray-800 font-semibold py-2 px-4 rounded-full inline-flex items-center"> | |
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-trash-2"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg> | |
</button> | |
</div> | |
</div> | |
))} | |
</Fragment> | |
); | |
}; | |
const Home = () => { | |
return ( | |
<div className="App"> | |
<div className="container mx-auto"> | |
<h3 className="text-center text-3xl mt-20 text-base leading-8 text-black font-bold tracking-wide uppercase"> | |
CRUD with React Context API and Hooks | |
</h3> | |
<div className="flex items-center mt-24 mb-10"> | |
<div className="flex-grow text-left px-4 py-2 m-2"> | |
<h5 className="text-gray-900 font-bold text-xl">Employee Listing</h5> | |
</div> | |
<div className="flex-grow text-right px-4 py-2 m-2"> | |
<Link to="/add"> | |
<button className="bg-green-400 hover:bg-green-500 text-white font-semibold py-2 px-4 rounded inline-flex items-center"> | |
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-plus-circle"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line></svg> | |
<span className="pl-2">Add Employee</span> | |
</button> | |
</Link> | |
</div> | |
</div> | |
<EmployeeList /> | |
</div> | |
</div> | |
); | |
}; | |
const AddEmployee = () => { | |
const history = useHistory(); | |
const { addEmployee } = useContext(GlobalContext); | |
const [name, setName] = useState(''); | |
const [location, setLocation] = useState(''); | |
const [designation, setDesignation] = useState(''); | |
const onSubmit = (e) => { | |
addEmployee({ | |
id: uuidv4(), | |
name, | |
location, | |
designation | |
}); | |
e.preventDefault(); | |
history.push("/"); | |
}; | |
return ( | |
<form className="w-full max-w-sm container mt-20 mx-auto" onSubmit={onSubmit}> | |
<div className="w-full mb-5"> | |
<label className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" htmlFor="name"> | |
Name of employee | |
</label> | |
<input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:text-gray-600" value={name} onChange={(e) => setName(e.target.value)} type="text" placeholder="Enter name" /> | |
</div> | |
<div className="w-full mb-5"> | |
<label className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" htmlFor="location"> | |
Location | |
</label> | |
<input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:text-gray-600 focus:shadow-outline" value={location} onChange={(e) => setLocation(e.target.value)} type="text" placeholder="Enter location" /> | |
</div> | |
<div className="w-full mb-5"> | |
<label className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" htmlFor="designation"> | |
Designation | |
</label> | |
<input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:text-gray-600" value={designation} onChange={(e) => setDesignation(e.target.value)} type="text" placeholder="Enter designation" /> | |
</div> | |
<div className="flex items-center justify-between"> | |
<button className="mt-5 bg-green-400 w-full hover:bg-green-500 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"> | |
Add Employee | |
</button> | |
</div> | |
<div className="text-center mt-4 text-gray-500"> | |
<Link to="/">Cancel</Link> | |
</div> | |
</form> | |
); | |
}; | |
const EditEmployee = () => { | |
const history = useHistory(); | |
const { editEmployee, employees } = useContext(GlobalContext); | |
const { id } = useParams(); | |
const [selectedUser, setSeletedUser] = useState({ id: null, name: '', designation: '', location: '' }); | |
useEffect(() => { | |
const selectedUser = employees.find((employee) => employee.id === id); | |
setSeletedUser(selectedUser); | |
}, []); | |
const onSubmit = (e) => { | |
editEmployee(selectedUser); | |
e.preventDefault(); | |
history.push('/'); | |
}; | |
const handleOnChange = (userKey, value) => setSeletedUser({ | |
...selectedUser, | |
[userKey]: value | |
}); | |
if (!selectedUser || !selectedUser.id) return <div>Employee not found</div> | |
return ( | |
<form className="w-full max-w-sm container mt-20 mx-auto" onSubmit={onSubmit}> | |
<div className="w-full mb-5"> | |
<label className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" htmlFor="name"> | |
Name of employee | |
</label> | |
<input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:text-gray-600 focus:shadow-outline" value={selectedUser.name} onChange={(e) => handleOnChange('name', e.target.value)} type="text" placeholder="Enter name" /> | |
</div> | |
<div className="w-full mb-5"> | |
<label className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" htmlFor="location"> | |
Location | |
</label> | |
<input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:text-gray-600 focus:shadow-outline" value={selectedUser.location} onChange={(e) => handleOnChange('location', e.target.value)} type="text" placeholder="Enter location" /> | |
</div> | |
<div className="w-full mb-5"> | |
<label className="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" htmlFor="designation"> | |
Designation | |
</label> | |
<input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:text-gray-600 focus:shadow-outline" value={selectedUser.designation} onChange={(e) => handleOnChange('designation', e.target.value)} type="text" placeholder="Enter designation" /> | |
</div> | |
<div className="flex items-center justify-between"> | |
<button className="block mt-5 bg-green-400 w-full hover:bg-green-500 text-white font-bold py-2 px-4 rounded focus:text-gray-600 focus:shadow-outline"> | |
Edit Employee | |
</button> | |
</div> | |
<div className="text-center mt-4 text-gray-500"> | |
<Link to="/"> | |
Cancel | |
</Link> | |
</div> | |
</form> | |
); | |
}; | |
const App = () => { | |
return ( | |
<BrowserRouter> | |
<GlobalState> | |
<Switch> | |
<Route path="/add" exact> | |
<AddEmployee /> | |
</Route> | |
<Route path="/edit/:id" exact> | |
<EditEmployee /> | |
</Route> | |
<Route> | |
<Home /> | |
</Route> | |
</Switch> | |
</GlobalState> | |
</BrowserRouter> | |
); | |
}; | |
ReactDOM.render( | |
<App />, | |
document.querySelector('#app') | |
); |
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router-dom/5.1.2/react-router-dom.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/uuidv4.min.js"></script> |
<link href="https://unpkg.com/[email protected]/dist/tailwind.min.css" rel="stylesheet" /> |