I'm having a synchronization issue with my axios.post call in reactjs. I've got an input area for a user to select a local file to submit into the axios.post, that file gets translated into an arraybuffer, then it's sent with an axios call to the microsoft faces api. If I select an image, then press the analyze button, the synchronization is off and it tries to make the post request without the array buffer. If I click it a second time, it makes the post correctly.
I'm fairly new to awaits and async, and any input on my problem would be a huge help. I thought about using a boolean flag, but want to learn how to better this function with the correct design .
import React, { useState } from "react";
import axios from "axios";
import { Button, Container, Row, Col } from "react-bootstrap";
import "../../styling/FacialRecognition.css";
import defaultImage from "../../assets/images/defaultImage.png";
function FacialRecognition() {
const [image, setImage] = useState("");
const [imageData, setImageData] = useState([]);
const [responseData, setResponseData] = useState([]);
const [dataLoad, setDataLoad] = useState(false);
//translates the local file to the array buffer, and sets the imageData.
function TranslateImageToArrayBuffer(imageToTranslate) {
let reader = new FileReader();
reader.readAsArrayBuffer(imageToTranslate);
reader.onloadend = () => {
setImageData(reader.result);
};
}
//Makes the call with the url,options, and the arraybuffer.
function ProcessImage(localImage) {
TranslateImageArrayBuffer(localImage);
console.log("ImageData: " + imageData);
var subscriptionKey = "<subscriptionkey>";
var uriBase =
"https://(urlgoeshere).cognitiveservices.azure.com/face/v1.0/detect";
const options = [
"returnFaceId=true",
"returnFaceLandmarks=true",
"returnFaceAttributes=age,gender,headPose,smile,facialHair,glasses,emotion,hair,makeup,accessories",
];
uriBase = uriBase.concat("?", options.join("&"));
// Perform the REST API call.
axios
.post(uriBase, imageData, {
headers: {
"Content-Type": "application/octet-stream",
"Ocp-Apim-Subscription-Key": subscriptionKey,
},
})
.then(function (response) {
// Display the image.
var blob = new Blob([imageData]);
var url = URL.createObjectURL(blob);
setDataLoad(true);
document.querySelector("#sourceImage").src = url;
console.log("Status text: " + response.status);
console.log("Status text: " + response.statusText);
console.log(response.data);
setResponseData(response.data);
})
.catch(function (error) {
console.log(error);
});
}
let value = responseData.map((e) => JSON.stringify(e, null, 2));
return (
<div>
<Container fluid="xl">
<Row>
<Col xs="auto">
<p>
Select a local image, and then click the <b>Analyze face button.</b>
</p>
</Col>
</Row>
<Row>
<Col x="auto">
<label>Image to analyze: </label>
<input
type="file"
name="inputImage"
id="inputImage"
autoComplete="off"
accept="image/png, image/jpeg"
onChange={(e) => setImage(e.target.files[0])}
style={{ width: "60%", marginLeft: "1vw" }}
/>
<Button
variant="info"
onClick={() => ProcessImage(image)}>
Analyze face
</Button>
</Col>
</Row>
<Row style={{ marginTop: "2vw" }}>
<Col xs="6">
<label>
<strong>Response</strong>
</label>
</Col>
<Col xs="auto">
<label>
<strong>Source Image</strong>
</label>
</Col>
</Row>
<Row>
<Col>
{dataLoad ? (
<textarea
id="responseTextArea"
className="UIInput"
style={{ width: "100%", height: "429px" }}
value={value}
readOnly={true}
disabled="disabled"></textarea>
) : (
<textarea
id="responseTextArea"
className="UIInput"
style={
dataLoad
? { display: "none" }
: { width: "100%", height: "auto" }
}
defaultValue="This field will display analyzed data on the image"
readOnly={true}
disabled="disabled"></textarea>
)}
</Col>
<Col xs="6">
{dataLoad ? (
<img
id="sourceImage"
alt=""
style={
dataLoad
? { width: "80%" }
: { display: "none" }
}
/>
) : (
<img
id="defaultImage"
src={defaultImage}
alt=""
style={
dataLoad
? { display: "none" }
: { width: "80%" }
}
/>
)}
</Col>
</Row>
</Container>
</div>
);
}
export default FacialRecognition;
I've also tried setting TranslateImageToArrayBuffer
as a async function, and putting the axios call inside a .then(response => {axios.post...})
without luck.
Edit: I've also tried wrapping my Translate function contents within a promise.
async function TranslateImageToArrayBuffer(imageToTranslate) {
return new Promise((resolve, reject) => {
var reader = new FileReader();
reader.onload = () => {
setImageData(reader.result);
resolve(imageData);
};
reader.onerror = () => {
reject(reader.result);
};
reader.readAsArrayBuffer(imageToTranslate);
});
}
and using a const contents = await TranslateImageToArrayBuffer(localImage);
line in my ProcessImage function to wait for the value. Still having the syncing issue.