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!
Okay! Let's start with the analysis (Caution: may have a lot to read)
Approach 1:
Consider an object,
Three places that accept the spread syntax are Function Arguments, Array and Object Literals. Only iterable values (array or string) can go into the first two places ie., Function Arguments and Array Literals.
Approach 2:
Now you know that there are only 3 places we could use spread syntax. Like your second example if I apply,
adding {...testObject}, object literal is valid OR use [Symbol.iterator]
Now you may connect this to conclude the Approach 3 which passes your complete object.
Hope this helps!