Maison > Questions et réponses > le corps du texte
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 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粉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 ( <>> ); }