Destructuring, Spread & Rest
Open any React file and you will see const { name } = props, [...items] and ({ a, ...rest }). This is the syntax that makes modern React code short. It looks similar for three different jobs, so it helps to separate them.
Definition
| Feature | What it does | Where it appears |
|---|---|---|
| Destructuring | Pulls values out of an object or array into variables | Left side of =, function parameters |
Spread (...) | Expands an array or object into separate items | Right side, inside [], {}, or a call |
Rest (...) | Collects the leftover items into one array or object | Left side, in a pattern or parameter list |
The same three dots mean "expand" or "collect" depending on the position. If it is creating or passing values, it is spread. If it is receiving values, it is rest.
Destructuring
1const user = { name: "Asha", age: 25, city: "Pune" };2 3const { name, age } = user; // by property name4const { city: town } = user; // rename5const { country = "India" } = user; // default if undefined6const { address: { zip } } = { address: { zip: 411001 } }; // nested7 8const [first, second] = ["a", "b", "c"]; // arrays use position9const [, , third] = ["a", "b", "c"]; // skip with commasSwapping two variables without a temp:
1[a, b] = [b, a];Defaults only apply when the value is undefined, not for null:
1const { x = 5 } = { x: null }; // x is null, not 5Rest
1const { name, ...others } = user; // others = { age: 25, city: "Pune" }2const [head, ...tail] = [1, 2, 3, 4]; // tail = [2, 3, 4]3 4function sum(...nums) { // nums is a real array5 return nums.reduce((a, b) => a + b, 0);6}7sum(1, 2, 3); // 6The rest element must be last. [...a, b] as a pattern is a syntax error.
Spread
1const a = [1, 2];2const b = [...a, 3, 4]; // [1, 2, 3, 4]3 4const base = { theme: "dark", size: "md" };5const next = { ...base, size: "lg" }; // later keys win: { theme: "dark", size: "lg" }6 7Math.max(...[3, 9, 2]); // 9, spread an array into argumentsOrder matters. In { ...base, size: "lg" } the new size overrides. In { size: "lg", ...base } the old one wins.
Spread makes a shallow copy. Nested objects are still shared, as we saw in the reference topic.
Here Is What Breaks
1const { a } = null; // TypeError: cannot destructure null2const { a } = undefined; // sameProtect against missing data with a default: const { a } = obj ?? {};.
Also, spreading a string or iterable into an object gives surprising results, and spread does not copy getters or non-enumerable properties the way you might expect.
In React
Props destructuring in the parameter list:
1function Card({ title, subtitle = "", children }) {2 return <div><h2>{title}</h2><p>{subtitle}</p>{children}</div>;3}Forwarding the rest of the props to a DOM element:
1function Button({ variant, ...rest }) {2 return <button className={variant} {...rest} />; // onClick, disabled, etc. pass through3}State updates with spread:
1setUser((prev) => ({ ...prev, name: "Ravi" }));2setItems((prev) => [...prev, newItem]);Hooks return arrays, so you rename them freely with array destructuring: const [count, setCount] = useState(0). A custom hook returning an object lets callers pick only what they need.
Summary
1Destructuring → pull values out (objects by name, arrays by position)2Rest → collect the leftovers, must be last3Spread → expand items into a new array, object or call4Same ... symbol → expand when creating, collect when receiving5Defaults → apply to undefined only6Spread copy → shallow7React → props destructuring, forwarding ...rest, immutable updatesSo far we used objects freely. Next we look at how objects inherit from each other, using prototypes.