DEV Community

Alex Howez
Alex Howez

Posted on

Breaking Down React Hooks (part 1)

I wanted to learn more about how React Hooks work under the hook, so I dove in the source code. In this blog we'll demistify all hooks and learn a bit about typed Javascript and React. Let's go!

Image description

useState

Let's start with React's most used hook

const [count, setCount] = useState(0);
Enter fullscreen mode Exit fullscreen mode

The first thing we notice is the "[var, setVar]" syntax. This is actually Javascript syntax that allows you to spread an array returned from a function. Check example below.

function fn() {
  let var1 = 'Hello', var2 = 'World', var3 = '!';
  return [var1, var2, var3];
}

const [var1, var2, var3] = fn();
console.log(var1); // Outputs: Hello
console.log(var2); // Outputs: World
console.log(var3); // Outputs: !
Enter fullscreen mode Exit fullscreen mode

Anyway, let's look at the useState source code.

export function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}
Enter fullscreen mode Exit fullscreen mode

That's the whole code for the useState function! It looks like Typescript, but actually Flow is used for types, originally developed at Facebook.

Looking again at how state is initialized

const [count, setCount] = useState(0);
Enter fullscreen mode Exit fullscreen mode

We see that we pass a generic value "0" which could be a number, string or even a function that returns a value. How do I know this? It's in the source!

function useState<S>(
  initialState: (() => S) | S,
)
Enter fullscreen mode Exit fullscreen mode

Here we get that a generic value is passed (research Generics if you don't know this concept) and it can be the generic value itself or a function.

Let's look further.

[S, Dispatch<BasicStateAction<S>>] 
Enter fullscreen mode Exit fullscreen mode

We also get that the return type is an array with 2 values, S (generic vlaue) and a Dispatch function that helps use update that state. This is what we broke down initially, remember?

We won't go into what a dispatcher is, yet. It's a very important concept but I'm already making this too long.
Basically it "dispatches" a change of state through a "reducer".

useEffect

export function useEffect(
  create: () => (() => void) | void,
  deps: Array<mixed> | void | null,
): void {
  const dispatcher = resolveDispatcher();
  return dispatcher.useEffect(create, deps);
}
Enter fullscreen mode Exit fullscreen mode

That is the whole code for useEffect. Yes, I know! It's short.

We can see almost the same format as in useState. But this time we take a function and an array as arguments. The function itself returns either another function or void.

If you look at an example of useEffect:

  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  }, []);
Enter fullscreen mode Exit fullscreen mode

We precisely see that useEffect() takes first a function as argument, and then an optional array.

In typescript you'd see the use of "?:" for optional types but here we see this which works for Flow:

 deps: Array<mixed> | void | null,
Enter fullscreen mode Exit fullscreen mode

We also see the use of dispatchers again and a dispatcher being called with "dispatcher.useEffect". Let me know if you want me to go more in depth on the dispatchers in a later blog.

More hooks

Let's see more hook examples

export function useContext<T>(Context: ReactContext<T>): T {
  const dispatcher = resolveDispatcher();
  return dispatcher.useContext(Context);
}
Enter fullscreen mode Exit fullscreen mode
export function useReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useReducer(reducer, initialArg, init);
}
Enter fullscreen mode Exit fullscreen mode
export function useCallback<T>(
  callback: T,
  deps: Array<mixed> | void | null,
): T {
  const dispatcher = resolveDispatcher();
  return dispatcher.useCallback(callback, deps);
}
Enter fullscreen mode Exit fullscreen mode

Now we notice that we can no longer avoid this anymore, we must know what a dispatcher is!

If you're interested, we'll continue in part 2.

Top comments (0)