Search code examples
javascriptreactjsreact-hooksreact-custom-hooks

Resize image React custom hook


I have a function that resizes the image uploaded by the user which is stored in state and will be sent to the backend.

const [file, setFile] = useState(null)

function dataURLtoFile(dataurl, filename) {
    let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n)
    while(n--){
        u8arr[n] = bstr.charCodeAt(n)
    }
    return new File([u8arr], filename, {type:mime});
}

const handleChange = (e) => {
    if (e.target.files[0].type.match(/image.*/)) {
        const reader = new FileReader()
        reader.readAsDataURL(e.target.files[0])
        reader.onloadend = (event) => {
            const image = new Image()
            image.onload = (el) => {
                const canvas = document.createElement('canvas')
                const max_size = 224
                let width = image.width
                let height = image.height
                if (width > height) {
                    if (width > max_size) {
                        height *= max_size / width
                        width = max_size
                    }
                } else {
                    if (height > max_size) {
                        width *= max_size / height
                        height = max_size
                    }
                }
                canvas.width = width
                canvas.height = height
                let ctx = canvas.getContext('2d', { alpha: false })
                ctx.drawImage(el.target, 0, 0, width, height)
                let dataUrl = canvas.toDataURL('image/jpeg', 1)
                setFile(dataURLtoFile(dataUrl, 'x-ray.png'))
            }
            image.src = event.target.result
        }
    } 
}

It works perfectly fine but it just feels too clumsy. I want to extract this part into a custom hook such as useImage but I don't exactly know how to implement it.


Solution

  • function dataURLtoFile(dataurl, filename) {
        let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
            bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n)
        while(n--){
            u8arr[n] = bstr.charCodeAt(n)
        }
        return new File([u8arr], filename, {type:mime});
    }
    
    const useImage = () => {
      const [file, setFile] = useState(null)
      const handleChange = useCallback((e) => {
        if (e.target.files[0].type.match(/image.*/)) {
            const reader = new FileReader()
            reader.readAsDataURL(e.target.files[0])
            reader.onloadend = (event) => {
                const image = new Image()
                image.onload = (el) => {
                    const canvas = document.createElement('canvas')
                    const max_size = 224
                    let width = image.width
                    let height = image.height
                    if (width > height) {
                        if (width > max_size) {
                            height *= max_size / width
                            width = max_size
                        }
                    } else {
                        if (height > max_size) {
                            width *= max_size / height
                            height = max_size
                        }
                    }
                    canvas.width = width
                    canvas.height = height
                    let ctx = canvas.getContext('2d', { alpha: false })
                    ctx.drawImage(el.target, 0, 0, width, height)
                    let dataUrl = canvas.toDataURL('image/jpeg', 1)
                    setFile(dataURLtoFile(dataUrl, 'x-ray.png'))
                }
                image.src = event.target.result
            }
        } 
      }, [])
    
      return { file, handleChange }
    }
    
    

    Then to use

    const { file, handleChange } = useImage()