Reputation: 440
I have this small custom hook which returns an Object.assign
result. In documentation it says that this returns an array, but the example below can be used with array destructuring AND object destructuring.
Code:
import { useState } from 'react';
const useStateExtended = (initialValue, initialLoading = false) => {
const [data, setData] = useState(initialValue);
const [loading, setLoading] = useState(initialLoading);
return Object.assign([data, setData, loading, setLoading], { data, setData, loading, setLoading })
};
export default useStateExtended;
This code can be used in the following ways:
const {data, setData, loading, setLoading} = useStateExtended({});
const [data, setData, loading, setLoading] = useStateExtended({});
How does this work? Why can I destructure both like it is an array and an object?
Secondly: How to type this in Typescript? Data types are:
data: any;
setData: (data: any) => void;
loading: boolean;
setLoading: (boolean) => void;
Upvotes: 2
Views: 1508
Reputation: 370679
You can use square-bracket destructuring on anything with an iterator, including, most notably, arrays.
You can use curly-bracket destructuring on any object (and arrays are objects).
When you do
Object.assign([data, setData, loading, setLoading], { data, setData, loading, setLoading })
You create an array which has both normal array indicies [0]
, [1]
, but also has properties .data
, .setData
, etc. This is pretty weird in Javascript, but since arrays are objects, and since objects can have arbitrary key-value pairs assigned to them, it's legal.
So, you can retrieve properties from the object either by using [
destructuring, invoking the array's iterator, or by using {
destructuring, extracting the named properties from the object.
That said, this isn't something you should do, because (as your question demonstrates), it's kind of confusing what's going on, and readability is probably the most important factor for code. Arrays should pretty much never have arbitrary key-value pairs (except in the case of a regular expression match, in which case the additional properties are generated by the interpreter itself).
To type it, use generics for the type of initialValue
so it can be passed to useState
, and declare the array as const
so that the array destructuring retrieves the right types in the right order:
const useStateExtended = <T extends unknown>(initialValue: T, initialLoading = false) => {
const [data, setData] = useState<T>(initialValue);
const [loading, setLoading] = useState(initialLoading);
return Object.assign([data, setData, loading, setLoading] as const, { data, setData, loading, setLoading })
};
Upvotes: 4