javascript 在React中添加状态时,项不可迭代

ktca8awb  于 2023-03-21  发布在  Java
关注(0)|答案(2)|浏览(113)

我目前正在尝试使用spread操作符将项目推送到React中的数组中,但是我得到了以下错误。

错误发生在PlacesForm.jsx中的PhotoUploader组件中。我已经包含了下面组件的代码。
PlacesForm.jsx:

import axios from 'axios'
import React, { useEffect, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import AccountNav from './AccountNav'
import Perks from './Perks'
import PhotosUploader from './PhotosUploader'

function PlacesForm() {
    const {id} = useParams()

    const [title, setTitle] = useState("")
    const [address, setAddress] = useState("")
    const [addedPhotos, setAddedPhotos] = useState([]) //state in question
    const [description, setDescription] = useState("")
    const [perks, setPerks] = useState([])
    const [extraInfo, setExtraInfo] = useState("")
    const [checkIn, setCheckIn] = useState("")
    const [checkOut, setCheckOut] = useState("")
    const [maxGuests, setMaxGuests] = useState(1)

    useEffect(() => {
        if (!id) return 
        const getPlace = async () => {
            try {
                const {data} = await axios.get(`/places/${id}`)
                setTitle(data.title)
                setAddress(data.address)
                setAddedPhotos(data.addedPhotos)
                setDescription(data.description)
                setPerks(data.perks)
                setExtraInfo(data.extraInfo)
                setCheckIn(data.checkIn)
                setCheckOut(data.checkOut)
                setMaxGuests(data.maxGuests)
            } catch (e) {
                console.log(e)
            }
        }
        getPlace()
    }, [id])

    const navigate = useNavigate()

    const inputHeader = (text) => {
        return (
            <h2 className='text-2xl mt-4'>{text}</h2>
        )
    }

    const inputDescription = (text) => {
        return (
            <p className="text-gray-500 text-sm">{text}</p>
        )
    }

    const preInput = (header, description) => {
        return (
            <>
                {inputHeader(header)}
                {inputDescription(description)}
            </>
        )
    }

    const handleSubmit = async (e) => {
        e.preventDefault()
        const placeData = {
            title,
            address, 
            addedPhotos, 
            description,
            perks,
            extraInfo,
            checkIn,
            checkOut,
            maxGuests
        }
        if (id) {
            //update 
            try {
                const res = await axios.put('/places', {
                    id,
                    ...placeData,
                })
                navigate('/account/places')
            } catch (error) {
                console.log(error)
            }
        } else {
            //create new place 
            try {
                const res = await axios.post('/places', placeData)
                navigate('/account/places')
            } catch (error) {
                console.log(error)
            }
        }

    }

  return (
    <>
        <div>
            <AccountNav />  
            <form onSubmit={handleSubmit}>
                {preInput('Title', 'Something catchy and memorable')} 
                <input 
                    type="text" 
                    placeholder="title" 
                    value={title} 
                    onChange={e => setTitle(e.target.value)} 
                />

                {preInput('Address', 'be specific')} 
                <input 
                    type="text" 
                    placeholder="address" 
                    value={address}
                    onChange={e => setAddress(e.target.value)}
                />

                
                {preInput('Photos', 'Best to have at least 4')}
                <PhotosUploader addedPhotos={addedPhotos} setAddedPhotos={setAddedPhotos} />

addPhotobyLink()和uploadPhoto()函数都发生了错误。post请求和后端工作正常,并且为两者定义了“filename”,但是“setAddedPhotos”部分出错。以下是组件本身(PhotoUploader.jsx):

import axios from 'axios'
import React, { useState } from 'react'

function PhotosUploader({addedPhotos, setAddedPhotos}) {
    const [photoLink, setPhotoLink] = useState("")
    

    const addPhotoByLink = async (e) => {
        e.preventDefault()
        try {
            const {data:filename} = await axios.post('/upload-by-link', {link: photoLink})
            setAddedPhotos(prev => [...prev, filename])

        } catch (error) {
            console.log(error)
        }
        setPhotoLink("")
    }

    const uploadPhoto = async (e) => {
        const files = e.target.files
        const data = new FormData()
        for (let i = 0; i < files.length; i++) {
            data.append('photos', files[i])
        }
        try {
            const {data:filenames} = await axios.post('/upload', data, {
                headers: {'Content-type': 'multipart/form-data'}
            })
            setAddedPhotos(prev => [...prev, ...filenames])
        } catch (error) {
            console.log(error)
        }
    }

  return (
    <>
        <div className='flex gap-2' >
            <input 
                value={photoLink} 
                onChange={e => setPhotoLink(e.target.value)} 
                type="text" 
                placeholder="Add using link ....jpg" 
            />
            <button 
                onClick={addPhotoByLink} 
                className='bg-gray-200 px-4 rounded-2xl'
            >
                Add&nbsp;photo
            </button>
        </div>                    

        <div className="grid gap-2 grid-cols-3 lg:grid-cols-6 md:grid-cols-4 mt-2">
            {addedPhotos?.length > 0 && addedPhotos.map((link) => (
                <div key={link} className="h-32 flex">
                    <img 
                        src={'http://localhost:3000/uploads/' + link} 
                        alt=""
                        className='rounded-2xl w-full object-cover position' 
                    />
                </div>
            ))}
            <label className="h-32 cursor-pointer flex items-center justify-center gap-2 border bg-transparent rounded-2xl p-2 text-2xl text-gray-600">
                <input type="file" multiple className='hidden' onChange={uploadPhoto}/>
                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-8 h-8">
                    <path strokeLinecap="round" strokeLinejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />
                </svg>
                Upload
            </label>
        </div>
    </>
  )
}

export default PhotosUploader

到目前为止,我试过:

setAddedPhotos(prev => [...prev, filename]) //didn't work
setAddedPhotos([...prev, filename]) //didn't work
setAddedPhotos([...addedPhotos, filename]) //didn't work, addPhotos isn't iterable error
setAddedPhotos(prev => [...addedPhotos, filename]) //didn't work
0md85ypi

0md85ypi1#

在React中,一般不建议向下传递状态更新器函数

这样做的一个原因是,它可能会导致不必要的重新渲染,并使代码更难推理。如果子组件接收setAddedPhotos函数作为prop并调用它,它将触发父组件中的状态更新,这反过来又会触发父组件及其所有子组件的重新渲染。如果组件树很大和/或状态经常更改,这可能会成为问题。因为它会导致很多不必要的重新渲染。
另一个原因是它可能会违反封装的原则,这是使用React的关键好处之一,封装意味着每个组件都应该有自己隔离的状态和行为,不应该直接修改其父组件或兄弟组件的状态,当子组件接收到状态更新器函数作为 prop 时,它可以访问并可以修改其父组件的状态这会使得更难推断组件树的行为。
你可以像这样传递行为函数,而不是将setAddedPhotos函数传递给子组件。

import React from 'react';

function PlacesForm() {
  const [addedPhotos, setAddedPhotos] = useState([]);

  const handleAddedPhotos = (newStates) => {
    setAddedPhotos(prev => [...prev, ...newStates]);
  };

  return (
    <PhotosUploader addedPhotos={addedPhotos} handleAddedPhotos={handleAddedPhotos} />
  );
}

function PhotosUploader({ addedPhotos, handleAddedPhotos }) {
    const uploadPhoto = async (e) => {
        const files = e.target.files
        const data = new FormData()
        for (let i = 0; i < files.length; i++) {
            data.append('photos', files[i])
        }
        try {
            const {data:filenames} = await axios.post('/upload', data, {
               headers: {'Content-type': 'multipart/form-data'}
            })
            handleAddedPhotos(filenames)
        } catch (error) {
            console.log(error)
         }
    }

  return (
       <></>
  );
}
kq0g1dla

kq0g1dla2#

它看起来像响应,data.addedPhoto on useEffect of PlacesForm.jsx是NULLString,只是不是数组。请尝试在那里写入控制台日志。如果data. addedPhoto是NULL,使其成为空数组,如果data.addedPhoto只是String,使其成为一个包含该字符串的元素数组。

相关问题