首页 > 解决方案 > How to submit forms using NextJS

问题描述

I am creating a product upload page for an e-commerce site using React/NextJS. Posting the form worked fine just using action='/admin/api/addproduct' in the form element, but I am struggling to get success when implementing a function to build and submit the form.

Here is the code for the upload form - AddProductForm.js:

import React, {useState} from 'react';
import { useRouter } from 'next/router'

import {Form} from 'react-bootstrap'

export default function AddProductForm({ }) {
     
    const router = useRouter()
    // userInput is for adding colours.
    const [userInput, setUserInput ] = useState('');

    // Form State Variables
    const [id, setId] = useState('')
    const [name, setName] = useState('')
    const [description, setDescription] = useState('')
    const [gender, setGender] = useState('')
    const [price, setPrice] = useState('')
    const [isOnSale, setIsOnSale] = useState('')
    const [discount, setDiscount] = useState('')
    const [colours, setColours ] = useState([]);
    const [sizes, setSizes] = useState([])
  
    
    const handleAddProduct = async event => {
      event.preventDefault();  
      const body = new FormData()
      body.append("name", name);
      body.append("description", description);
      body.append("gender", gender);
      body.append("id", id);
      body.append("price", price);
      body.append("isOnSale", isOnSale);
      body.append("discount", discount);
      colours.forEach( (colour, index) => {
        body.append(`colour-${index}`, colour)
      })
     
      const response = await fetch(
            "/api/addproduct", 
            {
                body: body,     
                headers: {
                "Content-Type": "application/x-www-form-urlencoded"
                
                },
                method: "POST"
        })
        .then(response => {
          alert(response['message'])
          router.push("/admin/inventory")
        })
        .catch(error => {
          console.log(error)
        })
    }
    
    const handleChangeColour = (e) => {
        setUserInput(e.currentTarget.value)
      }
      
    const handleRemoveColour = (e) => {
        e.preventDefault();
        const colourId = e.target.getAttribute("name")
        setColours(colours.filter(colour => colour.id != colourId));   
    }
    
    const addColour = (userInput) => {
        let copy = [...colours];
        
        setColours(copy);
        copy.push({id: colours.length + 1, colour: userInput});
    }

    const handleAddColour = (e) => {
        e.preventDefault();
        addColour(userInput);
        setUserInput("");
    }
     const handleChangeSizes = (e) => {
         alert("hello")
     }

    return (

            <Form id="add-item-form" onSubmit={handleAddProduct}>
              <div className="row">
                <div className ="col-lg-6">
                  <Form.Group className="mb-3" controlId="formProductId">
                    <Form.Control type="text" name="formProductId" placeholder="Enter Product Id" onChange={e => setId(e.target.value)} required/>
                  </Form.Group>
                  <Form.Group className="mb-3" controlId="formProductName">
                    <Form.Control name="formProductName" type="text" placeholder="Enter Product Name" onChange={e => setName(e.target.value)}required/>
                  </Form.Group>
                  <Form.Group className="mb-3" controlId="formProductGender">
                  <Form.Select name="formProductGender" aria-label="Gender Select" onChange={e => setGender(e.target.value)} required>
                    <option>Is this male, female, or both</option>
                    <option value="1">Male</option>
                    <option value="2">Female</option>
                    <option value="3">Unisex</option>
                  </Form.Select>
                  </Form.Group>
                  <Form.Group className="mb-3" controlId="formProductPrice" >
                    <Form.Control name="formProductPrice" type="number" placeholder="Enter Product Price" onChange={e => setPrice(e.target.value)} required/>
                  </Form.Group>
                  <Form.Group className="mb-3" controlId="formProductDiscount">
                    <Form.Control name="formProductDiscount" type="number" placeholder="Enter Current Discount (in %)" onChange={e => setDiscount(e.target.value)} required/>
                  </Form.Group>
                  <Form.Group className="mb-3" controlId="formProductIsOnSale">
                    <Form.Select name="formProductIsOnSale" aria-label="Sale Select" onChange={e => setIsOnSale(e.target.value)} required>
                      <option>Is this product on sale?</option>
                      <option value="1">Yes</option>
                      <option value="2">No</option>
                    </Form.Select>
                  </Form.Group>
                </div>
                <div className ="col-lg-6">
                  <Form.Group className="mb-3 " controlId="formProductDescription">
                    <Form.Control name="formProductDescription" type="text" placeholder="Enter Product Description" onChange={e => setDescription(e.target.value)} required/>
                  </Form.Group>
                  <Form.Group className="mb-3 " controlId="formProductSizes">
                    <Form.Label>Available Sizes</Form.Label>
                    <div className="row">
                      <div className="col-sm-2"></div>
                      <div className="col-sm-8">
                        <div className="row">
                          {['xs', 's', 'm', 'l', 'xl', 'xxl'].map(function(size, index){
                            return   (                      
                              <div className="col" key={index}>
                                <Form.Check 
                                  type='checkbox'
                                  id={`${size}-checkbox`}
                                  name={`${size}-checkbox`}
                                  label={`${size}`}
                                  onChange={handleChangeSizes}
                                />
                              </div>
                            )})}
                        </div>
                      </div>
                      <div className="col-sm-2"></div>
                    </div>
                  </Form.Group>
                  <Form.Group className="mb-3" controlId="formProductColours">
                    <div className="row">
                      <div className="col">
                        <Form.Label className="ml-5">Available Colours</Form.Label>
                      </div>
                      <div className="col">
                        <input value={userInput} type="text" onChange={handleChangeColour} placeholder="Enter Colour..."/>
                      </div>
                      <div className="col pb-1">
                        <div className="btn btn-outline-success btn-sm mb-1" onClick={handleAddColour}>+</div>
                      </div>
                    </div>
                    {colours.map(colour => {
                      return (
                        <>
                        <div className="row" key={colour.id}>
                          <div className="col">
                            <div > {colour.colour} </div>
                          </div>
                          <div className="col">
                          <div className="col pb-1">
                            <div name={colour.id} class="btn btn-outline-success btn-sm mb-1" onClick={handleRemoveColour}>-</div>
                          </div>
                          </div>
                        </div>
                        
                        <input type="hidden" name={`colour-{colour.colour}`}></input>
                        </>
                      )
                    })}
                  </Form.Group>
                </div>
              </div>
              <button type="submit" className="btn btn-outline-success">
                Add Product
              </button>
            </Form>

    )
}

And here is the api code addproduct.js

import {connectToDatabase} from '../../lib/mongodb'


async function addProduct(item){

    const { db } = await connectToDatabase();
    const resp = await db
        .collection("stock")
        .insertOne(item)   
    }

const post = async (req, res) => {
    var colourList=[]
    var sizeList=[]

    Object.keys(req.body).forEach( key => {
        
        if (key.substring(0,7) == "colour-"){
            colourList.push(req.body[key])
        }
        if (key.substring(0,14) == "size-checkbox-"){
            sizeList.push(key.substring(14))
        }
    })
    // Extract Form Data
    var newItem = JSON.stringify( {
        id : req.body['id'],
        name : req.body['name'],
        description : req.body['description'],
        gender : req.body['gender'],
        starRating : null,
        price : req.body['price'],
        isOnSale : req.body['isOnSale'],
        discount : req.body['discount'],
        colours : colourList,
        sizes : sizeList,

    })
    console.log(newItem)
    // //add the stock to the db
    // const resp = await addProduct(newItem)
    res.status(201).send({ message: 'Item Added' })
    return


};


export default (req, res) => {
    req.method === "POST"
      ? post(req, res)
      : req.method === "PUT"
      ? console.log("PUT")
      : req.method === "DELETE"
      ? console.log("DELETE")
      : req.method === "GET"
      ? console.log("GET")
      : res.status(404).send("");
  };

In the addproduct api call there is a console.log to print the new item. The output is as follows

{"starRating":null,"colours":[],"sizes":[]}

Which is also confusing, why is it only printing out those 3 attributes?

If I console.log(req.body) this is what I get

[Object: null prototype] {
  '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5\r\nContent-Disposition: form-data; name': '"name"\r\n' +
    '\r\n' +
    'ew\r\n' +
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5\r\n' +
    'Content-Disposition: form-data; name="description"\r\n' +
    '\r\n' +
    'asdadas\r\n' +
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5\r\n' +
    'Content-Disposition: form-data; name="gender"\r\n' +
    '\r\n' +
    '1\r\n' +
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5\r\n' +
    'Content-Disposition: form-data; name="id"\r\n' +
    '\r\n' +
    'weqwe\r\n' +
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5\r\n' +
    'Content-Disposition: form-data; name="price"\r\n' +
    '\r\n' +
    '323\r\n' +
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5\r\n' +
    'Content-Disposition: form-data; name="isOnSale"\r\n' +
    '\r\n' +
    '1\r\n' +
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5\r\n' +
    'Content-Disposition: form-data; name="discount"\r\n' +
    '\r\n' +
    '32\r\n' +
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5\r\n' +
    'Content-Disposition: form-data; name="colour-0"\r\n' +
    '\r\n' +
    '[object Object]\r\n' +
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5--\r\n'
}

Additionally, in the handleAddProduct function, 'const response = await fetch', I refer to response in the '.then' section, but this is not referring to the same response object declared previously and thus the message is not showing on the alert after attempting to add anitem.

标签: javascriptreactjsnext.js

解决方案


推荐阅读