I am currently working on a React project and encountered an interesting issue related to shallow copying objects. In my component, I have a form where I collect data, and I noticed that different methods of shallow copying the data object result in different behaviors.
Here are the three approaches I've tried:
Approach 1 (works)
const handleSubmit = async (e) => { e.preventDefault(); actions.updateEmail(...Object.values(localEmailData)); console.log(store); }; Approach 2 (works but with console error)
const handleSubmit = async (e) => { e.preventDefault(); actions.updateEmail(...localEmailData); console.log(store); }; The console error reads:
Uncaught (in promise) TypeError: Invalid attempt to spread non-iterable instance. In order to be iterable, non-array objects must have a [Symbol.iterator]() method. Approach 3 (won't work)
const handleSubmit = async (e) => { e.preventDefault(); actions.updateEmail(localEmailData); console.log(store); }; Can someone explain the differences between these methods, and why Approach 2, although working, triggers a console error? Additionally, why doesn't Approach 3 work?
My React component
[…] export const Home = () => { const { store, actions } = useContext(Context); const [localEmailData, setLocalEmailData] = useState({ projectName: store.email.nombreDelProyecto, unsubscribeUrl: store.email.unsubscribeUrl, header: { link1: store.email.header.link1, image1: store.email.header.image1, alt1: store.email.header.alt1, link2: store.email.header.link2, image2: store.email.header.image2, alt2: store.email.header.alt2, link3: store.email.header.link3, image3: store.email.header.image3, alt3: store.email.header.alt3, }, cta: { image: store.email.cta.image, link: store.email.cta.link, },[…] }); const handleInputChange = (e) => { setLocalEmailData({ localEmailData, [e.target.name]: e.target.value }); console.log("localEmailData", localEmailData); console.log("Submitting form with data:", localEmailData); }; const handleSubmit = async (e) => { e.preventDefault(); actions.updateEmail(...Object.values(localEmailData)); console.log(store); }; return ( <div className="mt-5"> <div className="d-flex justify-content-center"> {/* Formulario de encabezado */} <Form className="w-50" onSubmit={handleSubmit}> […] <div className="d-flex justify-content-center"> <Button type="submit" className="generar-correo"> Validate & Generate </Button> <Link to="/pocuromailbuilder" className="generar-correo"> <span>Go to </span> <span style={{ fontStyle: 'italic' }}>output</span> </Link> </div> </Form> </div> </div> ); }; Also, my action at flux.js is:
updateEmail: (newData) => { setStore({ ...getStore(), email: { ...getStore().email, ...newData, } }); }, Any insights or explanations would be greatly appreciated. Thank you!
someFunction(...localEmailData)syntax iflocalEmailDatais an array, not an object. This is how you are trying to use it in "approach 2", so the error is absolutely expected, and I'm amazed that you say it "works". Approach 1 though passes an array of the values (tryconsole.log(Object.Values(localEmailData))to see this), which is perfectly fine to use...on in this way. Note that you don't actually need to shallow copy with...here:actions.updateEmail(Object.values(localEmailData))will do exactly the same (Object.valuesreturns a new reference).updateEmailis doing. You've shown us the code but that isn't at all clear without context. Still, there's enough there to make all of your approaches look wrong - firstly it only takes one argument (newData), whereas spreading an array will pass it many arguments, so your function will ignore all but the first of those. (And withObject.valuesyou have no practical idea which order the values will be in.) Also it does...newDatainside an object literal, so that is expected to be an object anyway.