recherche

Maison  >  Questions et réponses  >  le corps du texte

Erreur de réaction : l'élément n'est pas itérable lors de l'ajout à l'état

J'essaie actuellement de placer des éléments dans un tableau dans React à l'aide de l'opérateur spread, mais j'obtiens l'erreur suivante.

L'erreur se produit dans le composant PhotoUploader dans PlacesForm.jsx. J'ai inclus le code dans le composant ci-dessous.

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} />

L'erreur se produit dans les fonctions addPhotobyLink() et uploadPhoto(). La demande de publication et le backend fonctionnent correctement et le "nom de fichier" est défini pour les deux, mais quelque chose ne va pas avec la partie "setAddedPhotos". Voici le composant lui-même (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

Jusqu'à présent, j'ai essayé :

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

P粉709644700P粉709644700250 Il y a quelques jours476

répondre à tous(1)je répondrai

  • P粉293550575

    P粉2935505752024-03-28 15:52:21

    Dans React, il n'est généralement pas recommandé de passer les fonctions de mise à jour du statut

    Une des raisons pour lesquelles cela se produit est que cela peut entraîner un nouveau rendu inutile et rendre le code plus difficile à raisonner. Si un composant enfant reçoit la fonction setAddedPhotos comme accessoire et l'appelle, il déclenchera une mise à jour de l'état dans le composant parent, qui à son tour déclenchera un nouveau rendu du composant parent et de tous ses enfants. Cela peut poser un problème si l'arborescence des composants est volumineuse et/ou si l'état change fréquemment, car cela peut entraîner de nombreux rendus inutiles.

    Une autre raison est que cela peut violer le principe d'encapsulation, qui est l'un des principaux avantages de l'utilisation de React. L'encapsulation signifie que chaque composant doit avoir son propre état et comportement indépendants et ne doit pas modifier directement l'état de ses composants parents ou frères. Lorsqu'un composant enfant reçoit une fonction de mise à jour d'état en tant qu'accessoire, il peut accéder et modifier l'état de son composant parent, ce qui peut rendre plus difficile le raisonnement sur le comportement de l'arborescence des composants.

    Au lieu de transmettre la fonction setAddedPhotos au composant enfant. Vous pouvez transmettre la fonction de comportement comme ceci.

    import React from 'react';
    
    function PlacesForm() {
      const [addedPhotos, setAddedPhotos] = useState([]);
    
      const handleAddedPhotos = (newStates) => {
        setAddedPhotos(prev => [...prev, ...newStates]);
      };
    
      return (
        
      );
    }
    
    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 (
           <>
      );
    }

    répondre
    0
  • Annulerrépondre