DEV Community

Cover image for Writing Your Own React Hooks, the Return Value
nathan amick
nathan amick

Posted on

Writing Your Own React Hooks, the Return Value

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()
Enter fullscreen mode Exit fullscreen mode

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} />
  )
}
Enter fullscreen mode Exit fullscreen mode

Returning an Array

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')
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

When to Return an Array

  • 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.


Returning an Object

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()
Enter fullscreen mode Exit fullscreen mode

When to Return an Object

  • 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.


Not destructuring

If a hook needs to return a larger number of values AND is expected to be used more than once, we don't have to destructure at all. This can occasionally be useful.

  const leftOptions = useItemOptions()
  const rightOptions = useItemOptions()

  return (
    <>
      <Column
        side="left"
        color={leftOptions.color}
        count={leftOptions.count}
        isValid={leftOptions.isValid}
        handleChangeColor={leftOptions.handleChangeColor}
        handleChangeCount={leftOptions.handleChangeCount}
      />
      <Column
        side="right"
        color={rightOptions.color}
        count={rightOptions.count}
        isValid={rightOptions.isValid}
        handleChangeColor={rightOptions.handleChangeColor}
        handleChangeCount={rightOptions.handleChangeCount}
      />
    </>
  )
Enter fullscreen mode Exit fullscreen mode

Not Destructuring with Spread

If we are careful with the naming of the properties in the object that our hook returns, we can use the spread syntax to dump props directly into our components.

The above component could be rewritten using the spread syntax like this:

  const leftOptions = useItemOptions()
  const rightOptions = useItemOptions()

  return (
    <>
      <Column side="left" {...leftOptions} />
      <Column side="right" {...rightOptions} />
    </>
  )
Enter fullscreen mode Exit fullscreen mode

(Apparently, Dev.to syntax highlighting for the spread operator in JSX is not yet supported, but this works.)


Conclusion

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!

Discussion (2)

Collapse
stu profile image
Stu Finn

Thanks for the very clear explanation of this - so helpful!

Collapse
helpmepls profile image
Lê Tấn Khôi

i've been looking all over the internet for this explanation. You made my day :D