Hello, I am trying to implement a loading spinner into my React app, but I have a problem with changing its state. It is supposed to appear when the data to display is being downloaded through Axios and then disappear. But right now it's just spinning forever. What am I doing wrong?
import React, { useState } from "react";
import customInstance from "../../api/axiosConfig";
import Grid from "@material-ui/core/Grid";
import Card from "@material-ui/core/Card";
import Box from "@material-ui/core/Box";
import Container from "@material-ui/core/Container";
import CardHeader from "@material-ui/core/CardHeader";
import TextField from "@material-ui/core/TextField";
import Moment from "moment";
import CheckOutlinedIcon from "@material-ui/icons/CheckOutlined";
import NotInterestedOutlinedIcon from "@material-ui/icons/NotInterestedOutlined";
import HourglassEmptyOutlinedIcon from "@material-ui/icons/HourglassEmptyOutlined";
import Button from "@material-ui/core/Button";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import GetAppIcon from "@material-ui/icons/GetApp";
import Modal from "@material-ui/core/Modal";
import Switch from "@material-ui/core/Switch";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import { Chip, FormControl, InputLabel, Typography } from "@material-ui/core";
import LoadingComponent from "../StatusPages/LoadingComponent";
function ProjectDetailsView(props) {
const [project, setProject] = useState();
const [ebit, setEbit] = useState();
const [ebitPlus, setEbitPlus] = useState();
const [ovi, setOvi] = useState();
const [bussinessCFO, setBussinessCFO] = useState();
const [bussinessController, setBussinessController] = useState();
const [bussinessOwner, setBussinessOwner] = useState();
const [backdropVisible, setBackdrop] = useState();
const [modalOpen, setModalOpen] = useState(false);
const [loading, setLoading] = useState(true);
let additionalProjectEntities = props.additionalEntities.map((entity) => ({
code: entity.entityCode,
name: entity.entityName,
}));
function handleSend(projectId, approver) {
let SendForApprovalCommand = {
ProjectId: projectId,
FirstApprover: approver,
};
customInstance
.post("Approval", SendForApprovalCommand)
.then((response) => {
alert("Approval sent");
})
.catch((error) => {
console.log(error);
alert("First Level Approval Needed");
});
}
function handleCancellation(project) {
project.isCanceled = true;
customInstance
.put("Project/" + project.projectId, project)
.then((res) => {
alert("Project has been Canceled");
})
.then(setModalOpen(false));
}
return loading ? <LoadingComponent /> : (
<div>
<Container maxWidth="lg">
<Modal
open={modalOpen}
onClose={() => setModalOpen(false)}
aria-labelledby="child-modal-title"
aria-describedby="child-modal-description"
>
<Box
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "background.paper",
border: "2px solid #000",
boxShadow: 24,
pt: 2,
px: 4,
pb: 3,
width: 600,
}}
>
<h2 id="child-modal-title">Confirmation</h2>
<p id="child-modal-description">
Are you sure you want to cancel this project?
</p>
<Button
variant="outlined"
style={{ color: "#6fbf73", borderColor: "#6fbf73" }}
onClick={() => handleCancellation(props.project)}
>
Yes, Cancel the Project
</Button>
<Button
variant="outlined"
onClick={() => setModalOpen(false)}
style={{ color: "#f50057", borderColor: "#f50057" }}
>
No, Don't Cancel the Project
</Button>
</Box>
</Modal>
<fieldset disabled>
<Grid container fluid="true" spacing={3}>
<Grid item xs={12}>
<h1 align="center">GP Project Sign-Off Approver Form</h1>
</Grid>
<Grid item xs={12} md={6}>
<Card>
<CardHeader
title="Project information"
align="center"
style={{ backgroundColor: "#002B45", color: "white" }}
/>
<Box p={3}>
<Grid item xs={12}>
<TextField
name="projectName"
label="Project Name"
variant="outlined"
fullWidth
margin="normal"
value={props.project.projectName || ""}
/>
</Grid>
<Grid item xs={12}>
<TextField
name="legalEntity"
label="Legal Entity Relevant for the Contract"
variant="outlined"
fullWidth
margin="normal"
value={props.project.entityName || ""}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
label="Additional Entities (if any)"
fullWidth
margin="normal"
InputProps={{
startAdornment: (
<Box sx={{ pt: 1 }}>
{additionalProjectEntities.map((entity) => {
return (
<ListItem key={entity.code}>
<Chip
label={entity.code + " - " + entity.name}
/>
</ListItem>
);
})}
</Box>
),
}}
/>
</Grid>
<Grid item xs={12}>
<TextField
name="valueImprovementType"
label="Value Improvement Type"
variant="outlined"
fullWidth
margin="normal"
value={props.project.valueImprovementTypeName || ""}
/>
</Grid>
<Grid item xs={12}>
<TextField
name="overallImprovementType"
label="Overall Improvement Type"
variant="outlined"
fullWidth
margin="normal"
value={props.project.overallImprovementTypeName || ""}
/>
</Grid>
<Grid item xs={12}>
<TextField
name="nameOfSuplier"
id="outlined-read-only-input"
label="Name of Selected Supplier"
variant="outlined"
fullWidth
margin="normal"
value={props.project.nameOfSuplier || ""}
/>
</Grid>
<Grid item xs={12}>
<Grid item xs={12} md={12}>
<TextField
name="contractStartDate"
id="date"
variant="outlined"
label="Start Date"
fullWidth
margin="normal"
value={
Moment(props.project.contractStartDate).format(
"YYYY-MM-DD"
) || ""
}
/>
</Grid>
<Grid item xs={12} md={12}>
<TextField
name="contractEndDate"
id="date"
variant="outlined"
label="End Date"
fullWidth
margin="normal"
value={
Moment(props.project.contractEndDate).format(
"YYYY-MM-DD"
) || ""
}
/>
</Grid>
</Grid>
</Box>
</Card>
<Card>
<CardHeader
title="Files"
align="center"
style={{ backgroundColor: "#002B45", color: "white" }}
/>
<Box p={3}>
<Grid item xs={12} md={6}>
<FileList files={props.files} />
</Grid>
</Box>
</Card>
</Grid>
<Grid item xs={12} md={6}>
<Grid item xs={12}>
<Card>
<CardHeader
title="Group Procurement Information"
align="center"
style={{ backgroundColor: "#002B45", color: "white" }}
/>
<Box p={3}>
<Grid item xs={12}>
<TextField
name="gpCategory"
label="GP Category"
variant="outlined"
fullWidth
margin="normal"
value={props.project.categoryName || ""}
/>
</Grid>
<Grid item xs={12}>
<TextField
name="gpSubCategory"
label="GP Sub-Category"
variant="outlined"
fullWidth
margin="normal"
value={props.project.subCategoryName || ""}
/>
</Grid>
<Grid item xs={12}>
<TextField
name="projectResponsible"
label="Project Responsible"
variant="outlined"
fullWidth
margin="normal"
value={props.project.projectResponsibleName || ""}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="Project number"
variant="outlined"
fullWidth
margin="normal"
value={props.project.fullProjectNumber || ""}
/>
</Grid>
<Grid item xs={12}>
<FormControlLabel
control={
<Switch
id="isEbitda"
name="isEbitda"
checked={props.project.isEbitda || 0}
color="primary"
/>
}
label="EBITDA"
/>
</Grid>
</Box>
</Card>
</Grid>
<Grid>
<Card>
<CardHeader
title="Sign-Off Information"
align="center"
style={{ backgroundColor: "#002B45", color: "white" }}
/>
<Box p={3}>
<Grid container item xs={12}>
<Grid item xs={6}>
<TextField
name="bussinessController"
label="Bussiness Controller"
variant="outlined"
fullWidth
margin="normal"
value={props.bussinessController.approverName || ""}
/>
</Grid>
<Grid item xs={3}>
<Button disabled>
<IconBar
approvalStatusId={
props.bussinessController.approvalStatusId
}
/>
</Button>
</Grid>
<Grid
item
xs={3}
className="item_styles_button"
alignItems="stretch"
style={{ display: "flex" }}
>
<Button
variant="outlined"
color="primary"
style={{ fontWeight: "bold", border: "2px solid" }}
onClick={() =>
handleSend(
props.project.projectId,
props.bussinessController.approverId
)
}
>
SEND
</Button>
</Grid>
</Grid>
<Grid container item xs={12}>
<Grid item xs={6}>
<TextField
name="bussinessOwner"
label="Bussiness Owner"
variant="outlined"
fullWidth
margin="normal"
value={props.bussinessOwner.approverName || ""}
/>
</Grid>
<Grid item xs={3}>
<Button disabled>
<IconBar
approvalStatusId={
props.bussinessOwner.approvalStatusId
}
/>
</Button>
</Grid>
<Grid
item
xs={3}
className="item_styles_button"
alignItems="stretch"
style={{ display: "flex" }}
>
<Button
variant="outlined"
color="primary"
style={{ fontWeight: "bold", border: "2px solid" }}
onClick={() =>
handleSend(
props.project.projectId,
props.bussinessOwner.approverId
)
}
>
SEND
</Button>
</Grid>
</Grid>
<Grid container item xs={12}>
<Grid item xs={6}>
<TextField
name="bussinessCFO"
label="Bussiness Divisional CFO/CFO"
variant="outlined"
fullWidth
margin="normal"
value={props.bussinessCFO.approverName || ""}
/>
</Grid>
<Grid item xs={3}>
<Button disabled>
<IconBar
approvalStatusId={
props.bussinessCFO.approvalStatusId
}
/>
</Button>
</Grid>
<Grid
item
xs={3}
className="item_styles_button"
alignItems="stretch"
style={{ display: "flex" }}
>
<Button
variant="outlined"
color="primary"
style={{ fontWeight: "bold", border: "2px solid" }}
onClick={() =>
handleSend(
props.project.projectId,
props.bussinessCFO.approverId
)
}
>
SEND
</Button>
</Grid>
</Grid>
</Box>
</Card>
</Grid>
</Grid>
<Grid item xs={12}>
<h2 align="center">Improvement Calculation</h2>
</Grid>
<Grid item xs={12} md={4} lg={4}>
<Card>
<Box p={3}>
<CardHeader
title="EBIT"
align="center"
style={{ backgroundColor: "#002B45", color: "white" }}
/>
<TextField
id="ebitTotalSpend"
label="Total spend / Baseline (DKK)"
variant="outlined"
fullWidth
margin="normal"
value={props.ebit.baseline || ""}
/>
<TextField
id="ebitTotalSpendNewContract"
label="Total spend new contract (DKK)"
variant="outlined"
fullWidth
margin="normal"
value={props.ebit.totalSpend || ""}
/>
<TextField
id="ebitImpactValue"
label="Impact (DKK)"
variant="outlined"
fullWidth
margin="normal"
value={props.ebit.impactValue || ""}
/>
<TextField
id="ImpactPercentage"
label="Impact (%)"
variant="outlined"
fullWidth
margin="normal"
value={props.ebit.impactPercentage || ""}
/>
<TextField
id="ebitdaDKK"
label="Ebitda (DKK)"
variant="outlined"
fullWidth
margin="normal"
value={props.ebit.ebitda || ""}
/>
</Box>
</Card>
</Grid>
<Grid item xs={12} md={4} lg={4}>
<Card>
<Box p={3}>
<CardHeader
title="EBIT+"
align="center"
style={{ backgroundColor: "#002B45", color: "white" }}
/>
<TextField
id="ebitPlusBaseline"
label="Total spend / Baseline (DKK)"
variant="outlined"
fullWidth
margin="normal"
value={props.ebitPlus.baseline || ""}
/>
<TextField
id="ebitPlusTotalSpend"
label="Total spend new contract (DKK)"
variant="outlined"
fullWidth
margin="normal"
value={props.ebitPlus.totalSpend || ""}
/>
<TextField
id="ebitPlusImpactValue"
label="Impact (DKK)"
variant="outlined"
fullWidth
margin="normal"
value={props.ebitPlus.impactValue || ""}
/>
<TextField
id="ebitPlusImpactPercentage"
label="Impact (%)"
variant="outlined"
fullWidth
margin="normal"
value={props.ebitPlus.impactPercentage || ""}
/>
</Box>
</Card>
</Grid>
<Grid item xs={12} md={4} lg={4}>
<Card>
<Box p={3}>
<CardHeader
title="OVI"
align="center"
style={{ backgroundColor: "#002B45", color: "white" }}
/>
<TextField
id="oviBaseline"
label="Total spend / Baseline (DKK)"
variant="outlined"
fullWidth
margin="normal"
value={props.ovi.baseline || ""}
/>
<TextField
id="oviTotalSpend"
label="Total spend new contract (DKK)"
variant="outlined"
fullWidth
margin="normal"
value={props.ovi.totalSpend || ""}
/>
<TextField
id="oviImpactValue"
label="Impact (DKK)"
variant="outlined"
fullWidth
margin="normal"
value={props.ovi.impactValue || ""}
/>
<TextField
id="oviImpactPercentage"
label="Impact (%)"
variant="outlined"
fullWidth
margin="normal"
value={props.ovi.impactPercentage || ""}
/>
</Box>
</Card>
</Grid>
<Grid item xs={12}>
<Card>
<Box p={3}>
<CardHeader
title="Description"
align="center"
style={{ backgroundColor: "#002B45", color: "white" }}
/>
<TextField
minRows={12}
multiline
maxRows={Infinity}
id="Description"
value={props.project.description}
variant="outlined"
fullWidth
margin="normal"
/>
</Box>
</Card>
</Grid>
<Grid
container
justifyContent="center"
style={{ height: "fit-content" }}
>
<Button
variant="outlined"
color="primary"
aria-label="outlined button group"
onClick={() => setModalOpen(true)}
>
Cancel Project
</Button>
</Grid>
</Grid>
</fieldset>
</Container>
</div>
);
}
function IconBar(props) {
switch (props.approvalStatusId) {
case "6dfcedf7-2acf-47a7-b446-a82a7e166ce9": //Approved
return <CheckOutlinedIcon />;
case "d6ac8e69-d48a-4365-9032-e66af4835020": //Pending
return <HourglassEmptyOutlinedIcon />;
case "5e04924b-a618-4f77-8f96-7ae0c94893ce": //Declined
return <NotInterestedOutlinedIcon />;
case "1d8fae49-58bb-4b98-98b6-c246d449a0bf": //None
return "Not sent";
}
return null;
}
function FileList(props) {
function downloadFile(projectFileId, fileName) {
customInstance.get("File/" + projectFileId).then((response) => {
const a = document.createElement("a");
a.href = response.data;
a.download = response.data.split("/").pop();
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
}
if (props.files === undefined) {
return null;
} else {
return (
<List>
{props.files.map((file) => (
<Grid container item xs={12}>
<Grid item xs={9}>
<ListItem>
<ListItemText primary={file.fileName} />
</ListItem>
</Grid>
<Grid item xs={3}>
<Button
onClick={() => downloadFile(file.projectFileId, file.fileName)}
>
<ListItemIcon>
<GetAppIcon />
</ListItemIcon>
</Button>
</Grid>
</Grid>
))}
</List>
);
}
}
export default class ProjectLockedView extends React.Component {
constructor(props) {
super(props);
this.state = {
project: {},
ebit: {},
ebitPlus: {},
ovi: {},
bussinessCFO: {},
bussinessController: {},
bussinessOwner: {},
files: [],
additionalEntities: [],
loading: true
};
}
componentDidMount() {
this.getProjectDetails();
this.getImprovementCalculations();
this.getProjectUsers();
this.getProjectFiles();
this.getAdditionalEntities();
this.setState({loading: false});
}
getProjectDetails() {
customInstance
.get("Project/" + this.props.id)
.then((response) => {
this.setState({ project: response.data });
})
.catch((error) => {
console.log(error);
});
}
getImprovementCalculations() {
customInstance
.get("ImprovementCalculation?projectId=" + this.props.id)
.then((response) => {
var ebit = response.data.filter((calculation) => {
return calculation.calculationType == "EBIT";
})[0];
if (ebit != undefined) {
this.setState({ ebit: ebit });
}
var ebitPlus = response.data.filter((calculation) => {
return calculation.calculationType == "EBIT+";
})[0];
if (ebitPlus != undefined) {
this.setState({ ebitPlus: ebitPlus });
}
var ovi = response.data.filter((calculation) => {
return calculation.calculationType == "OVI";
})[0];
if (ovi != undefined) {
this.setState({ ovi: ovi });
}
});
}
getProjectUsers() {
customInstance
.get("ProjectUser?projectId=" + this.props.id)
.then((response) => {
var controller = response.data.filter((user) => {
return user.roleName == "Bussiness Controller";
})[0];
var cfo = response.data.filter((user) => {
return user.roleName == "Bussiness CFO";
})[0];
var owner = response.data.filter((user) => {
return user.roleName == "Bussiness Owner";
})[0];
if (cfo !== undefined) {
this.setState({ bussinessCFO: cfo });
}
if (controller !== undefined) {
this.setState({ bussinessController: controller });
}
if (owner !== undefined) {
this.setState({ bussinessOwner: owner });
}
});
}
getProjectFiles() {
customInstance.get("File?projectId=" + this.props.id).then((response) => {
this.setState({ files: response.data });
});
}
getAdditionalEntities() {
customInstance
.get("ProjectEntity?projectId=" + this.props.id)
.then((response) => {
this.setState({ additionalEntities: response.data });
});
}
render() {
return (
<div>
<ProjectDetailsView
project={this.state.project}
bussinessCFO={this.state.bussinessCFO}
bussinessController={this.state.bussinessController}
bussinessOwner={this.state.bussinessOwner}
ebit={this.state.ebit}
ebitPlus={this.state.ebitPlus}
ovi={this.state.ovi}
files={this.state.files}
additionalEntities={this.state.additionalEntities}
/>
</div>
);
}
}
What I have tried:
I am trying to change the state inside the componentDidMount function, but it's not changing anything:
componentDidMount() {
this.getProjectDetails();
this.getImprovementCalculations();
this.getProjectUsers();
this.getProjectFiles();
this.getAdditionalEntities();
this.setState({loading: false});
}