Как отправлять формы с помощью NextJS

#javascript #reactjs #next.js

Вопрос:

Я создаю страницу загрузки продукта для сайта электронной коммерции с помощью React/NextJS. Публикация формы работала нормально, просто используя action=’/admin/api/addproduct’ в элементе формы, но я изо всех сил пытаюсь добиться успеха при реализации функции для создания и отправки формы.

Вот код для формы загрузки — 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>

    )
}
 

А вот код api 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("");
  };
 

В вызове api addproduct есть консоль.войдите в журнал, чтобы распечатать новый элемент. Результат выглядит следующим образом

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

Что также сбивает с толку, почему он выводит только эти 3 атрибута?

Если я console.log(req.body), это то, что я получаю

 [Object: null prototype] {
  '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5rnContent-Disposition: form-data; name': '"name"rn'  
    'rn'  
    'ewrn'  
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5rn'  
    'Content-Disposition: form-data; name="description"rn'  
    'rn'  
    'asdadasrn'  
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5rn'  
    'Content-Disposition: form-data; name="gender"rn'  
    'rn'  
    '1rn'  
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5rn'  
    'Content-Disposition: form-data; name="id"rn'  
    'rn'  
    'weqwern'  
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5rn'  
    'Content-Disposition: form-data; name="price"rn'  
    'rn'  
    '323rn'  
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5rn'  
    'Content-Disposition: form-data; name="isOnSale"rn'  
    'rn'  
    '1rn'  
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5rn'  
    'Content-Disposition: form-data; name="discount"rn'  
    'rn'  
    '32rn'  
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5rn'  
    'Content-Disposition: form-data; name="colour-0"rn'  
    'rn'  
    '[object Object]rn'  
    '------WebKitFormBoundaryrsGiVVNnm3aU1Ww5--rn'
}
 

Кроме того, в функции handleAddProduct «const response = ожидание выборки» я ссылаюсь на ответ в разделе». затем», но это не относится к тому же объекту ответа, объявленному ранее, и, следовательно, сообщение не отображается в предупреждении после попытки добавить элемент.

Комментарии:

1. Кажется req.body , что его не существует. Не могли бы вы попробовать что-нибудь console.log(req) и console.log(res) посмотреть, что на самом деле передается?

2. Я обновил вопрос, чтобы включить результат console.log(req.body), который показывает тело, я использую const body = new FormData() для создания тела.

3. Хорошо, поэтому я думаю, что сначала вам нужно преобразовать FormData() объект обратно в обычный объект, чтобы он снова стал доступным. Можете ли вы попробовать let info = Object.fromEntries(req.body.entries()) , а затем соответствующим образом изменить другие значения