Inertia + React Image Crop: Image is null First Submit, Everything Okay Second Submit
Hello, I have this problem I believe resides with React and its asynchronous nature. Can you please take a look at the code below and point out where I'm going wrong?
The form data is sent to the Laravel backend as expected. So, no problem with that side of things.
But the first time I submit the image file is null on the server but not the second time I submit. Not sure why this is. Do you have more experience with React and help me out please?
I must have the cropped image uploaded on the first submit. That's the expected behavior.
import { useEffect, useState } from "react";
import { Head, usePage } from "@inertiajs/react"; import Authenticated from "../../../Layouts/Authenticated"; import ImageCropper from "../../../Shared/Photos/ImageCropper";
export default function Photo({ auth }) { const { flash } = usePage().props;
return (
<>
<Authenticated user={auth.user} header={"Dashboard :: Create Photo"}>
<Head title="Dashboard :: Create Photo" />
{flash.success && (
<div className="alert">{flash.success}</div>
)}
{flash.error && (
<div className="alert">{flash.error}</div>
)}
<ImageCropper />
</Authenticated>
</>
);
}
import { useEffect, useState } from "react"; import { useForm, router } from "@inertiajs/react"; import Cropper from "react-cropper"; import "cropperjs/dist/cropper.css";
export default function ImageCropper() { const [url, setUrl] = useState(); const [cropper, setCropper] = useState(); const { data, setData, post, processing, progress, errors } = useForm({ filename: "", caption: "This is a caption", description: "This is a description", image: null, });
function getUrl(e) {
if(e.target.files) {
var url = URL.createObjectURL(e.target.files[0]);
var filename = e.target.files[0].name;
setUrl(url);
setData(values => ({
...values,
filename: filename,
image: null,
}));
}
}
function handleSubmit(e) {
e.preventDefault();
if(cropper) {
cropper.getCroppedCanvas().toBlob((blob) => {
const file = new File([blob], data.filename, { type: "image/png" });
setData(values => ({
...values,
image: file,
}));
post(route("dashboard.photos.store"));
}, "image/png", 1.0);
}
}
return (
<>
<form onSubmit={ handleSubmit }>
<label htmlFor="caption">Caption</label>
<input
type="text"
id="caption"
value={ data.caption }
autoComplete="off"
placeholder="Enter a caption"
onChange={e => setData("caption", e.target.value)}
/>
{errors.caption && <div>{errors.caption}</div>}
<label htmlFor="description">Description</label>
<input
type="text"
id="description"
value={ data.description }
autoComplete="off"
placeholder="Enter a description"
onChange={e => setData("description", e.target.value)}
/>
{errors.description && <div>{errors.description}</div>}
<label htmlFor="image">Image</label>
<input
type="file"
id="image"
autoComplete="off"
placeholder="Upload a file"
accept="image/jpeg, image/jpg, image/png"
onChange={getUrl}
/>
{errors.image && <div>{errors.image}</div>}
<button disabled={ processing }>Crop Image</button>
<Cropper
src={url}
style={{ height: "100%", width: "auto" }}
initialAspectRatio={1/1}
aspectRatio={1/1}
minCropBoxHeight={384}
minCropBoxWidth={384}
responsive={true}
guides={true}
checkOrientation={false}
checkCrossOrigin={true}
center={true}
dragMode={"move"}
cropBoxResizable={false}
background={true}
onInitialized={(instance) => {
setCropper(instance);
}}
autoCropArea={1}
autoCrop={true}
/>
</form>
</>
);
}
Not that it should matter I am using the Cropper Js package. The cropper side of things is working perfectly, and the cropped image is stored locally on the file system on the second submit.
Just that little niggle with the first submit something is not quite right and the file information is lost or delayed on the form data.
Thanks in advance.
Please or to participate in this conversation.