Search code examples
reactjsreact-hooksmultipartform-datareact-bootstraprtk-query

React Image Uploader with React-Bootstrap and RTK Query and Nodejs Backend with Express and Formidable


Problem - I have used the 'react-bootstrap' library to create a react component which handles displaying and uploading an image. At the point where I submit - the

I have a function called handleChange. Before I attempt to set the formData with useState I log the file data like so:

File {name: 'me.png', lastModified: 1686403734161, lastModifiedDate: Sat Jun 10 2023 09:28:54 GMT-0400 (Eastern Daylight Time), webkitRelativePath: '', size: 4326448, …}
lastModified: 1686403734161
lastModifiedDate: Sat Jun 10 2023 09:28:54 GMT-0400 (Eastern Daylight Time)
[[Prototype]]: Object
name: "me.png"
size: 4326448
type: "image/png"
webkitRelativePath: ""
[[Prototype]]: File

After this data is logged to the console I then instantiate a FormData object then append the file to it like so:

let data = new FormData() data.append('file', e.target.files[0]) setFormData(data)

After I set the form data I then have an async function where I call the async function called uploadFile. This is the function generated from RTK Query. Before I call this I log formData to the console and it is empty like so:

FormData {}

Here is my component code in full:

import React, { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { Col, Container, Form, Image, Row } from 'react-bootstrap'
import NextButton from 'src/features/Buttons/NextButton'
import { useUploadFileMutation } from 'src/appState/fileApi'
import { PROFILE_LINKS } from 'src/app/App.constants'

export default function Pics() {
    const navigate = useNavigate()
    const [file, setFile] = useState<File>()
    const [formData, setFormData] = useState<FormData>()
    const [imageURL, setImageURL] = useState<string>()
    const [
        uploadFile,
        {
            data: uploadFileData,
            isSuccess: isUploadFileSuccess,
            isError: isUploadFileError,
            error: uploadFileError,
        },
    ] = useUploadFileMutation()

    function handleChange(e: any) {
        if (e.target.files[0]) {
            console.log('e.target.files[0]', e.target.files[0])
            let data = new FormData()
            data.append('file', e.target.files[0])
            setFormData(data)
            setFile(e.target.files[0])
        }
    }

    async function handleFinish() {
        if (formData) {
            console.log('handleFinish', formData)
            await uploadFile(formData)
        }
    }

    useEffect(() => {
        if (!file) return
        const newImageURL = URL.createObjectURL(file)
        setImageURL(newImageURL)
    }, [file])

    useEffect(() => {
        if (isUploadFileSuccess) {
            navigate(PROFILE_LINKS.INDEX.to)
        }
    })

    return (
        <div className="pics mb-3" data-testid="pics">
            <Container fluid>
                <Row>
                    <Col>
                        {imageURL && (
                            <Image
                                alt="myProfilePic"
                                className="mb-4 shadow-sm border-primary"
                                src={imageURL}
                                width="200"
                                rounded
                                fluid
                            />
                        )}
                    </Col>
                </Row>
                <Form className="mb-3" encType="multipart/form-data">
                    <Row className="mb-5">
                        <Col>
                            <Form.Group controlId="pics">
                                <Form.Control
                                    accept="image/png, image/gif, image/jpeg"
                                    size="lg"
                                    type="file"
                                    onChange={handleChange}
                                />
                            </Form.Group>
                        </Col>
                    </Row>
                    <Row>
                        <Col>
                            <NextButton
                                handleNext={handleFinish}
                                text="Finish"
                            />
                        </Col>
                    </Row>
                </Form>
            </Container>
        </div>
    )
}

I tried to search for a solution on google and in stack overflow


Solution

  • I found a solution of how to properly log the formData object to the console...I just don't understand why but here it is

    console.log(Object.fromEntries(formData))

    I found the answer in this Stackoverflow post

    Now I just need to figure out how to send the formData from RTK Query to my nodejs / express backend API...any help would be nice since I have not worked with formData before.

    My backend API has the following controller:

    export const upload = async (req, res) => {
      console.log(0, req)
      try {
        const data = await parse(req)
        console.log('data', data)
        if (data) {
          res.status(200).json({ message: 'Upload success', data })
        } else {
          res.status(404).json({ message: "Session expired. Log back in." });
        }    
      } catch (error) {
        res.status(500).json({ message: "Something went wrong", error });
        console.log(error);
      }
    };
    

    My controller has a utility function parse() that parses the file using a Formidable and some AWS S3 plugins.