Home > Enterprise >  POST FormData to JSON object array with ReacStrap Form and Express
POST FormData to JSON object array with ReacStrap Form and Express

Time:03-17

I'm currently learning the MERN stack and I'm very new to React and Express. My current task is to use an API I previously created and update the JSON data using React.

I created a form component using ReactStrap and now I need POST that data to the JSON object array of my API.

I'm using FormData and so far I've been able to access and display the data correctly but I can't seem so figure out how to update or add any data. With my current code I keep on getting

Uncaught TypeError: Failed to construct 'FormData': parameter 1 is not of type 'HTMLFormElement'.

I've tried troubleshooting for this error but none of the answers I find seem to be applicable and as I'm just starting out with Express and APIs I'm not exactly sure what to look for. Any assistance would be great.

**I'm including a lot of code because I might be overlooking a small detail and want to create a full picture.

My Form component

import React from "react";
import Form from "react-bootstrap/Form";
import Modal from "react-bootstrap/Modal";
import AppButton from "./Button";

const WebForm = ({
  show,
  setShow,
  formTitle,
  onSubmit,
  onChange,
  btnValue,
  projID,
  idVal,
  titleName,
  descriptionName,
  projURLName,
  titleVal,
  descriptionVal,
  projURLVal,
}) => {
  return (
    <div>
      <Modal
        show={show}
        onHide={() => setShow(false)}
        dialogClassName="modal-90w"
        aria-labelledby="example-custom-modal-styling-title"
      >
        <Modal.Dialog>
          <Modal.Header closeButton>
            <Modal.Title>{formTitle}</Modal.Title>
          </Modal.Header>

          <Modal.Body>
            <Form onSubmit={onSubmit}>
              <Form.Group className="mb-3">
                <Form.Label>Project Name</Form.Label>
                <Form.Control
                  type="number"
                  placeholder="Project ID"
                  id="projID"
                  name={projID}
                  defaultValue={idVal}
                  onChange={onChange}
                />
                <Form.Text className="text-muted">
                  Please use ALL CAPS for the Project Name.
                </Form.Text>
              </Form.Group>

              <Form.Group className="mb-3">
                <Form.Label>Project Name</Form.Label>
                <Form.Control
                  type="text"
                  placeholder="Project Name"
                  id="projTitle"
                  name={titleName}
                  defaultValue={titleVal}
                  onChange={onChange}
                />
                <Form.Text className="text-muted">
                  Please use ALL CAPS for the Project Name.
                </Form.Text>
              </Form.Group>

              <Form.Group className="mb-3">
                <Form.Label>Project Description</Form.Label>
                <Form.Control
                  type="text"
                  placeholder="Enter Description"
                  id="projDescription"
                  name={descriptionName}
                  defaultValue={descriptionVal}
                  onChange={onChange}
                />
              </Form.Group>
              <Form.Group className="mb-3">
                <Form.Label>Web Address</Form.Label>
                <Form.Control
                  type="text"
                  placeholder="Project Web-Address"
                  id="projURL"
                  name={projURLName}
                  defaultValue={projURLVal}
                  onChange={onChange}
                />
              </Form.Group>
              <AppButton value={btnValue} type="submit" />
            </Form>
          </Modal.Body>
        </Modal.Dialog>
      </Modal>
    </div>
  );
};

export default WebForm;

The main UI component

import React, { useState, useEffect, useRef } from "react";
import Carousel from "react-bootstrap/Carousel";
import AppButton from "./Button";
import DispCard from "./Card";
import WebForm from "./Form";

const ProjectGrid = () => {
  const [index, setIndex] = useState(0);
  const [showEdit, setShowEdit] = useState(false);
  const [showAdd, setShowAdd] = useState(false);
  const [project, setProject] = useState([{}]);
  const [formValue, setValue] = useState("");
  const form = useRef(null);
  console.log(project);
  console.log(form);

  const fetchProjects = async () => {
    const res = await fetch("/projects");
    res.json().then((res) => setProject(res));
  };

  useEffect(() => {
    fetchProjects();
  }, []);

  const handleSelect = (selectedIndex, e) => {
    setIndex(selectedIndex);
  };

  const handleChange = (e) => {
    setValue(e.target.value);
    console.log(formValue);
  };

  const submit = (e) => {
    e.preventDefault();

    const data = new FormData(form.current);
    console.log(project);
    console.log(data);
    fetch("/projects", {
      method: "POST",
      body: data,
    })
      .then((res) => res.json())
      .then((json) => setProject(json.project));
    console.log(project);
    console.log(data);
  };

  return (
    <div>
      <Carousel activeIndex={index} onSelect={handleSelect} interval={null}>
        {project.map((item, index) => {
          return (
            <Carousel.Item key={index}>
              <DispCard
                onEdit={() => setShowEdit(true)}
                title={item.title}
                description={item.description}
                URL={item.URL}
              />
              <Carousel.Caption>
                <h3>{item.title}</h3>
                <p>
                  <br />
                  <br />
                </p>
              </Carousel.Caption>
            </Carousel.Item>
          );
        })}
      </Carousel>
      {project.map((item, index) => (
        <>
          {console.log(item.title)}
          <WebForm
            show={showEdit}
            setShow={setShowEdit}
            formTitle="Edit Project Details"
            titleName="item[][title]"
            descriptionName="item[][description]"
            projURLName="item[][URL]"
            titleVal={item.title}
            descriptionVal={item.description}
            projURLVal={item.URL}
            onChange={(e) => handleChange(e)}
            onSubmit={submit}
            btnValue="Done"
          />
          <WebForm
            show={showAdd}
            setShow={setShowAdd}
            formTitle="Add a New Project"
            titleName="project[title]"
            descriptionName="project[description]"
            projURLName="project[URL]"
            titleVal={project.title}
            descriptionVal={project.description}
            projURLVal={project.URL}
            onChange={(e) => handleChange(e)}
            onSubmit={submit}
            btnValue="Add"
          />
        </>
      ))}
      <AppButton onClick={() => setShowAdd(true)} value={"Add"} />
    </div>
  );
};

export default ProjectGrid;

The API

const express = require("express");
const app = express();
const fileHandler = require("fs");

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get("/projects", (req, res) => {
  fileHandler.readFile("web_project.json", (err, data) => {
    if (err) res.send("File not found. First post to create file.");
    else res.send(`${data}`);
  });
});

app.post("/projects", (req, res) => {
  res.send();
  fileHandler.readFile("web_project.json", function (err, data) {
    if (err) res.send("File not found. First post to create file.");
    else {
      let json = JSON.parse(data);
      json.push({
        id: req.body.id,
        title: req.body.title,
        description: req.body.description,
        URL: req.body.URL,
      });

      console.log(JSON.stringify(json));

      fileHandler.writeFile("web_project.json", JSON.stringify(json), (err) => {
        if (err) throw err;
      });
    }
  });
});

app.put("/projects/:id", (req, res) => {
  const idParams = req.params.id;
  console.log(idParams);

  fileHandler.readFile("web_project.json", (err, data) => {
    if (err) res.send("File not found. First post to create file.");
    else {
      let project = JSON.parse(data);
      //console.log(project);

      for (let i = project.length - 1; i > -1; i--) {
        console.log(project[i].id);
        if (project[i].id === parseInt(idParams)) {
          project = req.body;
          console.log(req.body);
        }
      }

      console.log(JSON.stringify(project));

      fileHandler.writeFile(
        "web_project.json",
        JSON.stringify(project),
        (err) => {
          if (err) throw err;
          res.send(JSON.stringify(project));
        }
      );
    }
  });
});

app.delete("/projects/:id", (req, res) => {
  res.send(req.params.id);

  const idParams = req.params.id;
  console.log(idParams);

  fileHandler.readFile("web_project.json", (err, data) => {
    if (err) res.send("File not found. First post to create file.");
    else {
      let project = JSON.parse(data);
      console.log(project);

      for (let i = project.length - 1; i > -1; i--) {
        console.log(project[i].id);
        if (project[i].id === parseInt(idParams)) {
          project.splice(i, 1);
        }
      }

      console.log(JSON.stringify(project));

      fileHandler.writeFile(
        "web_project.json",
        JSON.stringify(project),
        (err) => {
          if (err) throw err;
        }
      );
    }
  });
});

const PORT = process.env.PORT || 3001;

app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});

//localhost:8080/api/web-projects?id=4&title=Online-store&description=johnny&URL=something

And finally the JSON file

[
  {
    "id": 1,
    "title": "React Game!",
    "description": "Tic tac toe game created using Create React app.",
    "URL": "https://protected-anchorage-65131.herokuapp.com/"
  },
  {
    "id": 3,
    "title": "Online Shop",
    "description": "Online store for a decor and design company.",
    "URL": "https://kondtoin.lsojdnf/kdkfsj"
  },
  {
    "id": 2,
    "title": "Online Portfolio",
    "description": "Online portfolio created with HTML, CSS and JavaScript.",
    "URL": "https://jc03ceige.github.io/JC_van_der_Merwe/"
  }
]

CodePudding user response:

The problem is on React side. In the submit method you're doing const data = new FormData(form.current);. But the value of form.current will always be null since you're never using the ref that you defined with this line const form = useRef(null);

Instead of using a ref I think the following should work

  const submit = (e) => {
    e.preventDefault();

    // const data = new FormData(form.current); remove
    // The event contains the target that triggered the submit event
    const data = new FormData(e.target);
    console.log(project);
    console.log(data);
    fetch("/projects", {
      method: "POST",
      body: data,
    })
      .then((res) => res.json())
      .then((json) => setProject(json.project));
    console.log(project);
    console.log(data);
  };

CodePudding user response:

When you create an instance of FormData you need to append data to the formData object. Use this solution

const data = new FormData()
data.append("username", username)

// Go ahead and append all your variables

On your Nodejs server, if you're not sending a file along with your form, do not send form data content-type to the server. Use json instead

  • Related