Raphael
Raphael

Reputation: 149

resizing an image before saving in state in React

I am trying to resize an image before saving it in state so that I can pass it into my payload for upload. The method below handles the resize

export const handleResize = (fileObject) => {
    const reader = new FileReader();
    const maxUploadFileSize = 1048576;
    const imageTypes = ['image/jpeg', 'image/png', 'image/gif'];
    if (fileObject.size > maxUploadFileSize && imageTypes.includes(fileObject.type)) {
        reader.addEventListener('load', () => {
            minifyImg(reader.result, 1000, fileObject.type, (data) => {
                const newFile = imageUrlToFile(data, fileObject.name);
                return newFile;
            });
        }, false);
    } else {
        return fileObject;
    }

    if (fileObject) {
        reader.readAsDataURL(fileObject);
    }
};

I am using the function above in my component with code below;

changeHandler(event) {
        const uploadFilesArray = Array.prototype.slice.call(event.target.files);
        const proofOfIdentityArr = [];
        const resizedImages = [];

        if (uploadFilesArray.length > 1) {
            this.setState({
                errText: 'Too many files selected.' +
                    'You can\'t upload more than one file at a time.',
            });
            setTimeout(() => {
                this.setState({
                    errText: '',
                });
            }, 5000);
            return;
        }
        uploadFilesArray.forEach((file) => {
            const fileUrl = window.URL.createObjectURL(file);
            const reader = new FileReader();
            const fileData = {
                fileUrl,
                name: file.name,
            };
            reader.onload = () => {
                fileData.imagePreviewUrl = reader.result;
            };
            reader.readAsDataURL(file);
            proofOfIdentityArr.push(fileData);
            
            //importing and using the resizing function
   
            const newFile = utils.handleResize(file));
            resizedImages.push(newFile);
        });
        this.setState({
            proofOfIdentity: proofOfIdentityArr,
            uploadFilesArray: resizedImages,
        });
    }

The function resizes the image but my problem is that it is asynchronous and returns with a resized image late after the state has already been set, so I get an undefined. How can I make my changeHandler function wait for the resize function to return its value before proceeding.

Upvotes: 0

Views: 441

Answers (1)

Nikita Chayka
Nikita Chayka

Reputation: 2137

First of all your handleResize function doesn't always return value. But generally what you need to do is to use callback in your handleResize (when image will get resize - handleResize will call passed function aka callback), instead of returning new image from there.

handleResize:

export const handleResize = (fileObject, callback) => {
const reader = new FileReader();
const maxUploadFileSize = 1048576;
const imageTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (fileObject.size > maxUploadFileSize && imageTypes.includes(fileObject.type)) {
    reader.addEventListener('load', () => {
        minifyImg(reader.result, 1000, fileObject.type, (data) => {
            const newFile = imageUrlToFile(data, fileObject.name);
            callback(newFile); //Invoke callback with new resized image
        });
    }, false);
} else {
    callback(fileObject); //Invoke callback with passed image (size is OK)
}

if (fileObject) {
    reader.readAsDataURL(fileObject);
}
};

changeHandler:

changeHandler(event) {
    const uploadFilesArray = Array.prototype.slice.call(event.target.files);
    const proofOfIdentityArr = [];
    const resizedImages = [];

    if (uploadFilesArray.length > 1) {
        this.setState({
            errText: 'Too many files selected.' +
                'You can\'t upload more than one file at a time.',
        });
        setTimeout(() => {
            this.setState({
                errText: '',
            });
        }, 5000);
        return;
    }
    uploadFilesArray.forEach((file) => {
        const fileUrl = window.URL.createObjectURL(file);
        const reader = new FileReader();
        const fileData = {
            fileUrl,
            name: file.name,
        };
        reader.onload = () => {
            fileData.imagePreviewUrl = reader.result;
        };
        reader.readAsDataURL(file);
        proofOfIdentityArr.push(fileData);
        
        //importing and using the resizing function
        // here we are using the callback - and adding new "resized" image to the state
        utils.handleResize(file,(resizedImage) => {
               this.setState((state) => { return { uploadFilesArray : 
                [...state.uploadFilesArray, resizedImage]} }); 
        });
        //resizedImages.push(newFile);
    });
    this.setState({
        proofOfIdentity: proofOfIdentityArr //,
        //uploadFilesArray: resizedImages, // - not here
    });
}

Upvotes: 1

Related Questions