I am new to React and I know about the concepts of Functional and Class components aswell as Hooks such as useEffect and useState; hence I am having troubles turning this knowledge into practice, as I am working on the following React component that makes a plain Javascript API Call which I now want to transfer into a "real" React API Call,using the State and Hooks.
My problem is the following: I want to render the employee-object that the API is returning, starting with the employee firstname and surname, then additional information.
The request to the API in plain Javascript goes well and returns the needed data; hence I am not really sure on what to set the state to by definition (0?false?something else?And on what does that depend on, how do I know?).
Here´s the code:
import React, {useEffect, useState} from 'react';
import { Link } from "react-router-dom";
import {
Container,
Row,
Col,
Card,
CardBody,
Table,
Button, Alert, Modal, ModalHeader, ModalBody
} from "reactstrap";
const ContactsList = (props) => {
let request = new XMLHttpRequest()
// Open a new connection, using the GET request on the URL endpoint
request.open('GET', 'https://somenet.net/employee', true)
request.onload = function () {
// Begin accessing JSON data here
let data = JSON.parse(this.response)
data.forEach((employee) => {
// Log each movie's title
console.log(employee.id, employee.firstname, employee.lastname, employee.performance_index, employee.min_customer_distance, employee.customer_distance_radius, employee.webfleet_obj_id, employee.default_employee_working_schedule_id)
})
}
// Send request
request.send()
let employees = [
{
id: 1, img: "Null", name: "David McHenry", designation: "UI/UX Designer", email: "david@skote.com", projects: "125",
skills: [
{ name: "Photoshop" },
{ name: "illustrator" }
]
}
]
const users = [
{
id: 1, img: "Null", name: "David McHenry", designation: "UI/UX Designer", email: "david@skote.com", projects: "125",
skills: [
{ name: "Photoshop" },
{ name: "illustrator" }
]
},
{
id: 2, img: avatar2, name: "Frank Kirk", designation: "Frontend Developer", email: "frank@skote.com", projects: "132",
skills: [
{ name: "Html" },
{ name: "Css" },
{ name: "2 + more" },
]
},
{
id: 3, img: avatar3, name: "Rafael Morales", designation: "Backend Developer", email: "Rafael@skote.com", projects: "1112",
skills: [
{ name: "Php" },
{ name: "Java" },
{ name: "Python" },
]
},
{
id: 4, img: "Null", name: "Mark Ellison", designation: "Full Stack Developer", email: "mark@skote.com", projects: "121",
skills: [
{ name: "Ruby" },
{ name: "Php" },
{ name: "2 + more" },
]
},
{
id: 5, img: avatar4, name: "Minnie Walter", designation: "Frontend Developer", email: "minnie@skote.com", projects: "145",
skills: [
{ name: "Html" },
{ name: "Css" },
{ name: "2 + more" },
]
},
{
id: 6, img: avatar5, name: "Shirley Smith", designation: "UI/UX Designer", email: "shirley@skote.com", projects: "136",
skills: [
{ name: "Photoshop" },
{ name: "UI/UX Designer" }
]
},
{
id: 7, img: "Null", name: "John Santiago", designation: "Full Stack Developer", email: "john@skote.com", projects: "125",
skills: [
{ name: "Ruby" },
{ name: "Php" },
{ name: "2 + more" },
]
},
{
id: 8, img: avatar7, name: "Colin Melton", designation: "Backend Developer", email: "colin@skote.com", projects: "136",
skills: [
{ name: "Php" },
{ name: "Java" },
{ name: "Python" },
]
},
];
const DefaultEvents = [{
id: 1,
title: 'Hey!',
start: new Date().setDate(new Date().getDate() + 1),
className: 'bg-warning text-white'
},
{
id: 2,
title: 'See John Deo',
start: new Date(),
end: new Date(),
className: 'bg-success text-white'
},
{
id: 3,
title: 'Meet John Deo',
start: new Date().setDate(new Date().getDate() + 8),
className: 'bg-info text-white'
},
{
id: 4,
title: 'Buy a Theme',
start: new Date().setDate(new Date().getDate() + 7),
className: 'bg-primary text-white'
}];
const DefaultCategories = [
{
id: 1,
title: 'New Theme Release',
type: 'success'
},
{
id: 2,
title: 'My Event',
type: 'info'
},
{
id: 3,
title: 'Meet Manager',
type: 'warning'
},
{
id: 4,
title: 'Report Error',
type: 'danger'
},
];
const event1= { id: 0, title: "", title_category: "", start: "", className: "", category: "", event_category: "" };
const [calendarEvents, setCalendarEvents] = useState(DefaultEvents);
const [categories, setCategories] = useState(DefaultCategories);
const [modal, setModal] = useState(false);
const [modal1, setModal1] = useState(false);
const [modalcategory, setModalcategory] = useState(false);
const [event, setEvent] = useState(event1);
const [selectedDay, setSelectedDay] = useState(0);
const title_category = false;
const calendarComponentRef = React.createRef();
useEffect(() => {
new Draggable(document.getElementById("external-events"), {
itemSelector: '.external-event',
});
});
/**
* Handling the modal state
*/
function toggle() {
setModal(!modal)
}
function toggle1() {
setModal1(!modal1)
}
function togglecategory() {
setModalcategory(!modalcategory)
}
/**
* Handling date click on calendar
*/
const handleDateClick = (arg) => {
setSelectedDay(arg);
toggle();
}
/**
* Handling click on event on calendar
*/
const handleEventClick = (arg) => {
const eventNew = arg.event;
const event_tmp = { id: eventNew.id, title: eventNew.title, title_category: eventNew.title_category, start: eventNew.start, className: eventNew.classNames, category: eventNew.classNames[0], event_category: eventNew.classNames[0] };
setEvent(event_tmp);
toggle1();
}
/**
* Handling submit event on event form
*/
const handleValidEventSubmit = (e, values) => {
var newEvent = {};
newEvent = {
id: calendarEvents.length + 1,
title: values['title'],
start: selectedDay ? selectedDay.date : new Date(),
className: values.category + ' text-white'
};
// save new event
setCalendarEvents(calendarEvents.concat(newEvent));
setSelectedDay(null);
toggle();
}
const handleValidEventSubmitEvent = (e, values) => {
var newEvent = {};
newEvent = { id: event.id, title: values.title, classNames: values.category + ' text-white', start: event.start };
//first, remove array item, which we want to edit
let filteredArray = calendarEvents.filter(item => item.id + "" !== event.id + "");
//then concat update item details
let NewArray = filteredArray.concat(newEvent);
//store to state
setCalendarEvents(NewArray);
setEvent(null);
setSelectedDay(null);
toggle1();
}
const handleValidEventSubmitcategory = (e, values) => {
var newEvent = {};
newEvent = {
id: calendarEvents.length + 1,
title: values['title_category'],
type: values.event_category
};
// categories.concat(newEvent);
setCategories(categories.concat(newEvent));
togglecategory();
}
/**
* On calendar drop event
*/
const onDrop = (event) => {
const draggedEl = event.draggedEl;
var newEvent = {
id: calendarEvents.length + 1,
title: draggedEl.innerText,
start: event.date,
className: draggedEl.getAttribute('data-type') + ' text-white'
};
// save new event
setCalendarEvents(calendarEvents.concat(newEvent));
}
return (
<React.Fragment>
<div className="page-content">
<Container fluid>
{/* Render Breadcrumbs */}
<Breadcrumbs title="Contacts" breadcrumbItem="Users List" />
<Card>
<CardBody>
<Row>
<Col lg={3}>
<Button color="primary" className="font-16 btn-block" onClick={() => togglecategory() }>
<i className="mdi mdi-plus-circle-outline"></i> Create New Event
</Button>
<div id="external-events" className="mt-3">
<p className="text-muted">Drag and drop your event or click in the calendar</p>
{categories.map((category, i) => {
return <Alert color={category.type}>{category.title} </Alert>
})}
</div>
</Col>
<Col className="col-lg-3">
<div className="table-responsive">
<Table className="table-centered table-nowrap table-hover">
<thead className="thead-light">
<tr>
<th scope="col" style={{ width: "70px" }}>#</th>
<th scope="col">Name</th>
</tr>
</thead>
<tbody>
{
employees.map((user, i) =>
<tr key={"_user_" + i} >
<td>
{
user.img === "Null"
? <div className="avatar-xs">
<span className="avatar-title rounded-circle">
{user.name.charAt(0)}
</span>
</div>
: <div>
<img className="rounded-circle avatar-xs" src={user.img} alt="" />
</div>
}
</td>
<td>
<h5 className="font-size-14 mb-1"><Link to="#" className="text-dark">{user.name}</Link></h5>
<p className="text-muted mb-0">{user.designation}</p>
</td>
</tr>
)
}
</tbody>
</Table>
</div>
</Col>
<Col className="col-lg-6">
{/* fullcalendar control */}
<FullCalendar ref={calendarComponentRef} defaultView="dayGridMonth" plugins={[BootstrapTheme, dayGridPlugin, interactionPlugin]}
slotDuration={'00:15:00'}
minTime={'08:00:00'}
maxTime={'19:00:00'}
handleWindowResize={true}
themeSystem="bootstrap"
header={{
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,dayGridWeek,dayGridDay'
}}
events={calendarEvents}
editable={true}
droppable={true}
eventLimit={true}
selectable={true}
dateClick={handleDateClick}
eventClick={handleEventClick}
drop={onDrop}
id="calendar" />
<button onClick={() => togglecategory() }
className="btn btn-secondary float-right btn-lg waves-effect btn btn-secondary">
Neuen Termin anlegen
</button>
{/* New event modal */}
<Modal isOpen={modal} toggle={() => toggle()} className="">
<ModalHeader toggle={() => toggle()} tag="h4">
Add Event
</ModalHeader>
<ModalBody>
<AvForm onValidSubmit={handleValidEventSubmit}>
<Row form>
<Col className="col-12">
<AvField name="title" label="Event Name" type="text" errorMessage="Invalid name" validate={{
required: { value: true }
}} value={event ? event.title : ''} />
</Col>
<Col className="col-12">
<AvField type="select" name="category" label="Select Category"
value={event ? event.category : 'bg-primary'}>
<option value="bg-danger">Danger</option>
<option value="bg-success">Success</option>
<option value="bg-primary">Primary</option>
<option value="bg-info">Info</option>
<option value="bg-dark">Dark</option>
<option value="bg-warning">Warning</option>
</AvField>
</Col>
</Row>
<Row>
<Col>
<div className="text-right">
<button type="button" className="btn btn-light mr-2" onClick={() => toggle()}>Close</button>
<button type="submit" className="btn btn-success save-event">Save</button>
</div>
</Col>
</Row>
</AvForm>
</ModalBody>
</Modal>
{/* edit event modal */}
<Modal isOpen={modal1} toggle={() => toggle1()} className="">
<ModalHeader toggle={() => toggle1()} tag="h4">
Edit Event
</ModalHeader>
<ModalBody>
<AvForm onValidSubmit={handleValidEventSubmitEvent}>
<Row form>
<Col className="col-12">
<AvField name="title" label="Event Name" type="text" errorMessage="Invalid name" validate={{
required: { value: true }
}} value={event ? event.title : ''} />
</Col>
<Col className="col-12">
<AvField type="select" name="category" label="Select Category"
value={event ? event.category : 'bg-primary'}>
<option value="bg-danger">Danger</option>
<option value="bg-success">Success</option>
<option value="bg-primary">Primary</option>
<option value="bg-info">Info</option>
<option value="bg-dark">Dark</option>
<option value="bg-warning">Warning</option>
</AvField>
</Col>
</Row>
<Row>
<Col>
<div className="text-right">
<button type="button" className="btn btn-light mr-2" onClick={() => toggle()}>Close</button>
<button type="submit" className="btn btn-success save-event">Save</button>
</div>
</Col>
</Row>
</AvForm>
</ModalBody>
</Modal>
<Modal isOpen={modalcategory} toggle={() => togglecategory()} className="">
<ModalHeader toggle={() => togglecategory()} tag="h4">
Add a category
</ModalHeader>
<ModalBody>
<AvForm onValidSubmit={handleValidEventSubmitcategory}>
<Row form>
<Col className="col-12">
<AvField name="title_category" label="Category Name" type="text" errorMessage="Invalid name" validate={{
required: { value: true }
}} value={title_category ? event.title_category : ''} />
</Col>
<Col className="col-12">
<AvField type="select" name="event_category" label="Choose Category Color"
value={event ? event.event_category : 'bg-primary'}>
<option value="bg-danger">Danger</option>
<option value="bg-success">Success</option>
<option value="bg-primary">Primary</option>
<option value="bg-info">Info</option>
<option value="bg-dark">Dark</option>
<option value="bg-warning">Warning</option>
</AvField>
</Col>
</Row>
<Row>
<Col>
<div className="text-right">
<button type="button" className="btn btn-light mr-2" onClick={() => togglecategory()}>Close</button>
<button type="submit" className="btn btn-success save-event">Save</button>
</div>
</Col>
</Row>
</AvForm>
</ModalBody>
</Modal>
</Col>
</Row>
</CardBody>
</Card>
</Container>
</div>
</React.Fragment>
);
}
export default ContactsList;
Any hints or help would be very much appreciated - thank you in advance!
A couple things:
useEffect(() => {
fetchData().then(response => {
const employees = JSON.parse(response)
setEmployees(employees)
})
}, [])
See also this question: ReactJS: how to call useEffect hook only once to fetch API data
Now that we have a proper way to fetch data, we get to the other question - what to set the state to by definition
. In other words, how to initialize the data. People may tell you that it can be anything, but really, if you're using an array (you're iterating of the employees object so I'm assuming it is an array), it's just good to initialize it with an empty array. This way the type doesn't change, and the JSX down below doesn't need additional conditional logic to handle different types of employees.
const [employees, setEmployees] = useState([])
I usually start with the useState calls first, and then call useEffect. Not sure if it breaks if you swap them around, actually. So the total code would be:
const fetchData = async () => {
const res = await fetch('https://swapi.dev/api/people/')
const json = await res.json()
return json.result
}
const ContactsList = props => {
const [employees, setEmployees] = useState([])
useEffect(() => {
fetchData().then(employees => {
setEmployees(employees)
})
}, [])
return (
<div>
{employees.map(employee => <div key={employee.id}>{employee.name}</div>)}
</div>
)
}
On a more general note: