DEV Community

Remon Fawzi
Remon Fawzi

Posted on

Why useState hook returns array and not object? Let's create a custom hook to see

Hello, Have you ever asked why returning arrays from functions became trendy nowadays?

As we all know react hooks like useState, useEffect or useRef could only be used in the top level of a component and can't be used inside a function, custom hooks are functions where we can use React hooks inside of them.

let's see our example without using custom hook ...

import React, { useState, useEffect } from "react";

const Form = () => {
  const [name, setName] = useState(
    JSON.parse(localStorage.getItem("name")) ?? ""
  );
  const [email, setEmail] = useState(
    JSON.parse(localStorage.getItem("email")) ?? ""
  );

  useEffect(() => {
    localStorage.setItem("name", JSON.stringify(name));
  }, [name]);
  useEffect(() => {
    localStorage.setItem("email", JSON.stringify(email));
  }, [email]);
  return (
    <form>
      <input type="text" value={name} onChange={e => setName(e.target.value)} />
      <input
        type="text"
        value={email}
        onChange={e => setEmail(e.target.value)}
      />
      <button>Register</button>
    </form>
  );
};

export default Form;
Enter fullscreen mode Exit fullscreen mode

So, We've a react component contains a form of two inputs, the target is to save user's inputs in localStorage and retrieve them when he comes back.

So We've a state for each input, which is initialized from stored value or an empty string, onChange we set state with the new value, and We've a useEffect to set the value to localStorage when changed.

This is fine, but let's build the same example using custom hook to see the difference ...

import { useState, useEffect } from "react";

export default function useStoreInput(storageKey) {
  const [value, setValue] = useState(
    JSON.parse(localStorage.getItem(storageKey)) ?? ""
  );

  useEffect(() => {
    localStorage.setItem(storageKey, JSON.stringify(value));
  }, [value, storageKey]);

  return [value, setValue];
}
Enter fullscreen mode Exit fullscreen mode

This is simply our custom hook, It's a normal function that uses React hooks, We pass localStorage key name to it, and it defines a state for us and initialize it with the value from localStorage as before, then it listens to this state and store it to localStorage when changed.

As we can see we've chosen to return array [value, setValue] and not an object, We'll see why ...

Let's see our component uses our new custom hook ...

import React from "react";
import useStoreInput from "./useStoreInput";

const Form = () => {
  const [name, setName] = useStoreInput("name");
  const [email, setEmail] = useStoreInput("email");

  return (
    <form>
      <input type="text" value={name} onChange={e => setName(e.target.value)} />
      <input
        type="text"
        value={email}
        onChange={e => setEmail(e.target.value)}
      />
      <button>Register</button>
    </form>
  );
};

export default Form;
Enter fullscreen mode Exit fullscreen mode

As we can see when using custom hooks we didn't see any code repetition in our component, that's because of two reasons ...

  1. custom hooks are great to extract component's logic in a simple function, it also makes our logic reusable for any other input.
  2. returning arrays make it easier and cleaner to destructure values, We only give names to the returned array elements.

If we've decided to return object instead of array, our component would look like this

import React from "react";
import useStoreInput from "./useStoreInput";

const Form = () => {
  const { value: name, setValue: setName } = useStoreInput("name");
  const { value: email, setValue: setEmail } = useStoreInput("email");

  return (
    <form>
      <input type="text" value={name} onChange={e => setName(e.target.value)} />
      <input
        type="text"
        value={email}
        onChange={e => setEmail(e.target.value)}
      />
      <button>Register</button>
    </form>
  );
};

export default Form;
Enter fullscreen mode Exit fullscreen mode

So every time I use the hook I'd tell it to rename value and setValue with new names.

This is exactly why useState() hook returns array and not object

const [counter, setCounter] = useState(0);
Enter fullscreen mode Exit fullscreen mode

So react creators have chosen to return array from the hook to make it easier to destructure the returned array and define new states.

It's not only about hooks or React or even JavaScript, You can think the same way if you use a language that supports destructuring.

Returning arrays is awesome but for sure it depends on your case, let's assume if we've a function that returns 10 properties but we don't always use all properties, in some cases We only use the 8th element, also maintaining code in this case would be so hard, so it's not always a right decision to return an array.

Thank you!

Top comments (0)