Reputation: 107
I am trying to get the react-easy-crop library working following this guy's tutorial: https://www.youtube.com/watch?v=RmiP1AY5HFM
However, around the middle of his video, he is able to show his image after selecting the file. In my code, I can see the image for 0.5 seconds, and it disappears because React Crashes with the log: "Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops."
I know there's an infinite loop somewhere but I cannot figure out where...I'm quite new to react.. please help!
const inputRef = React.useRef();
const triggerFileSelectPopup = () => inputRef.current.click();
const [image, setImage] = useState(null);
const [croppedArea, setCroppedArea] = useState(null);
const [crop, setCrop] = useState({x:0, y:0});
const [zoom, setZoom] = useState(1)
const onCropComplete = () => {}
const onSelectFile = (event) => {
if (event.target.files && event.target.files.length > 0) {
const reader = new FileReader()
reader.readAsDataURL(event.target.files[0])
reader.addEventListener('load', ()=> {
console.log(reader.result);
setImage(reader.result);
});
}
};
const CropContainer = () => {
return (
<div className='CropContainer'>
<div className='container-cropper'>
<Cropper
image={image}
crop={crop}
zoom={zoom}
aspect={1}
onCropChange={setCrop}
onZoomChange={setZoom}
onCropComplete={onCropComplete}
/>
<Slider />
</div>
<div className='container-buttons'>
<input
type='file'
accept="image/jpg, image/jpeg, image/png"
ref={inputRef}
style={{display:'none'}}
onChange={onSelectFile}
/>
<Button
variant='contained'
onClick={triggerFileSelectPopup}>Choose New Image
</Button>
<Button
variant='contained'>Save
</Button>
</div>
</div>
);
}
return (
<div className="NewDiv">
<CropContainer />
</div>
);
Upvotes: 2
Views: 681
Reputation: 709
i had this problem myself, when i was opening the modal to crop the image i was getting infinite console.log from onCropComplete, because i was using an image saved in redux state i had to put the image inside a normal state first then put the image into Cropper
const IMG = useSelector((state: RootStateOrAny) => state.create.photoRaw)
const [image, setImage] = useState(URL.createObjectURL(IMG))
const [crop, setCrop] = useState<Point>({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const onCropComplete = useCallback(
(croppedArea: Area, croppedAreaPixels: Area) => {
console.log(croppedArea, croppedAreaPixels);
}, []);
<Cropper
image={image}
crop={crop}
zoom={zoom}
aspect={1 / 1}
onCropChange={setCrop}
onCropComplete={onCropComplete}
onZoomChange={setZoom} />
Upvotes: 0
Reputation: 203051
It seems likely that something the nested CropContainer
component does causes the outer component to rerender. When the outer component rerenders, it declares a new CropContainer
component, which unmounts the previous "instance" and mounts a new one. This is why it is considered anti-pattern to define React components within other react components.
You can either move the JSX that CropContainer
returns, as you've done
return (
<div className="NewDiv">
<div className="CropContainer">
<div className="container-cropper">
<Cropper
image={image}
crop={crop}
zoom={zoom}
aspect={1}
onCropChange={setCrop}
onZoomChange={setZoom}
onCropComplete={onCropComplete}
/>
<Slider />
</div>
<div className="container-buttons">
<input
type="file"
accept="image/jpg, image/jpeg, image/png"
ref={inputRef}
style={{ display: "none" }}
onChange={onSelectFile}
/>
<Button variant="contained" onClick={triggerFileSelectPopup}>
Choose New Image
</Button>
<Button variant="contained">Save</Button>
</div>
</div>
</div>
);
or you can move the CropContainer
declaration outside the outer component. Pass all the callbacks and such in as props.
const CropContainer = ({
crop,
image,
inputRef,
onCropChange,
onCropComplete,
onSelectFile,
setCrop,
setZoom,
triggerFileSelectPopup,
zoom
}) => {
return (
<div className="CropContainer">
<div className="container-cropper">
<Cropper
image={image}
crop={crop}
zoom={zoom}
aspect={1}
onCropChange={setCrop}
onZoomChange={setZoom}
onCropComplete={onCropComplete}
/>
<Slider />
</div>
<div className="container-buttons">
<input
type="file"
accept="image/jpg, image/jpeg, image/png"
ref={inputRef}
style={{ display: "none" }}
onChange={onSelectFile}
/>
<Button variant="contained" onClick={triggerFileSelectPopup}>
Choose New Image
</Button>
<Button variant="contained">Save</Button>
</div>
</div>
);
};
...
return (
<div className="NewDiv">
<CropContainer /* pass all those props here */ />
</div>
);
Upvotes: 1