useState and its friends

Aug 13, 2022

Did you know that useState & useReducer are the same function inside react source codes?

Of course, react exports different interfaces for you to call both useState and useReducer functions.

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

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);

But during the mount and update phases, react calls the same functions beneath the surface:

function updateState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  // we are calling undateReducer in updateState with a default reducer called `basicStateReducer`
  return updateReducer(basicStateReducer, (initialState: any));

//Here the basicStateReducer is:
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
  return typeof action === 'function' ? action(state) : action;

In that case, you can create a useReducer hook on your own:

import { useState } from 'react'

// All we need to do is to pass a customized reducer to replace the default `basicStateReducer` callback function:
export function useReducer(reducer, initialState) {
    const [state, setState] = useState(initialState)

    const dispatch = (action) => {
        setState(prevState => reducer(prevState, action))

    return [state, dispatch]

Besides of above, we could even use useState to implement useRef:

function useRef(initialValue) {
  const [ref, unused] = useState({ current: initialValue });
  return ref;

When you are coding with react, useRef is the kind of function that is called “Escape Hatches”.

So, if we do not replace the state with the setState method, the information inside would always be there during renders.

Do Not Remain Silent

Back To Top