JavaScript can only return one value from a function. However, destructuring in modern JavaScript makes this largely irrelevant.
We can return multiple values in arrays or objects from a function and instantly destructure them. And, as you know, a React hook is just a convention-following function.
const [one, two] = useNumbers()const { a, b, c } = useAlphabet()
So why use one method over another?
Let's look at some different use cases and explore why we might want to return an array vs. an object.
First, it should be noted, that we don't have to return anything. The built in React.useEffect
hook does just this, (returning undefined
actually).
We can also just return a single value, such as a string or integer. An example of this might be a hook that subscribes to a WebSocket API and returns a continuously updating value representing the current number of users online:
function OnlineUsers() {const userCount = useOnlineUserCount()return <Users count={userCount} />}
A hook that has a very general use case benefits from exporting return values as an array. A great example of this is actually the built-in React.useState
hook. Exporting an array makes it easy to customize the names of the state variables and their setters. Unique names enable us to use the hook repeatedly.
const [color, setColor] = useState('MintCream')const [width, setWidth] = useState('100vw')
A slightly-contrived example of a custom hook that would benefit from returning an array might be CSS builder that also holds on to some state.
const [leftStyle, setLeftTheme] = useStyleBuilder('dank-kitty')const [rightStyle, setRightTheme] = useStyleBuilder('no-drama-llama')
The number of values that need to be returned is low. Order is significant and remembering the order of a bunch of values takes extra brain cycles.
The hook is expected to be used more than once in the same component. Although we can rename properties when destructuring an object, the simpler syntax for custom-named values returned from an array makes more sense.
A hook that has a more specialized use case and returns a larger number of values may benefit by returning an object.
Object destructuring doesn't rely on ordering and it is easier to ignore values that are not needed. An example might be a hook with 3 or 4 state values along with handler functions:
const { color, count, isValid, handleChangeColor, handleChangeCount } =useItemOptions()
The number of values that need to be returned is high. We don't have to remember the order or even use all the values when returning an object.
The hook is not expected to be used more than once in the same component. We can use the original property names when destructuring an object returned from a hook that will only be used once in a component.
We can return multiple values in several different ways that make sense at different times. Hopefully, this will give you some ideas about how to make your hooks more readable and easier to understand.
We have a bunch of options in our toolbelt here. There is overlap and there are trade-offs, so play around and figure out what makes sense for your application. And have fun!