useRef and useMemo

How they work by looking at their code

Intro

We are going to dive into useRef and useMemo. If you are unfamiliar with either of these hooks I recommend reading about them here: useRef, useMemo

useRef

Let's start with useRef because it is the simpler one (see on github)

-- CODE language-js -- function useRef<T>(initialValue: T): {|current: T|} {
currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
workInProgressHook = createWorkInProgressHook();
const previousRef = workInProgressHook.memoizedState; 
if (previousRef === null) {
const ref = {current: initialValue};
if (__DEV__) {
Object.seal(ref);
}
workInProgressHook.memoizedState = ref;
return ref;
} else {   
return previousRef; 
}
}


First, let’s simplify the code to make it easier to understand:


-- CODE language-js -- function useRef(initialValue) {
  const previousRef = workInProgressHook.memoizedState;
  if (previousRef === null) {
    const ref = {current: initialValue};
    workInProgressHook.memoizedState = ref;
    return ref;
  } else {
    return previousRef;
  }
}


MemoizedState is nothing but an object. It’s a fancy name that doesn’t apply in this situation because nothing is memoized. All this is doing is storing some data. That is it. Nothing more. Creating a ref gives you a place to store whatever you want. It’s called useRef or use reference because the ref holds a reference to your storage. useRef returns {current:value}. Current is whatever you want it to be. And current can be set at any time to a new value.


-- CODE language-js -- Const ref = useRef(3); //{current:3}
Ref.current = 4 //{current:4}
Ref.current = {foo: ‘poo’} //{current: {foo: ‘poo’}}


There you go. useRef is simple.


useMemo

Now for useMemo (see on github)

-- CODE language-js -- function useMemo<T>(nextCreate: () => T, deps: Array<mixed> | void | null): T {
  currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
  workInProgressHook = createWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  if (workInProgressHook !== null) {
    const prevState = workInProgressHook.memoizedState;
    if (prevState !== null) {
      if (nextDeps !== null) {
        const prevDeps = prevState[1];
        if (areHookInputsEqual(nextDeps, prevDeps)) {
          return prevState[0];
        }
      }
    }
  }
  if (__DEV__) {
    isInHookUserCodeInDev = true;
  }
  const nextValue = nextCreate();
  if (__DEV__) {
    isInHookUserCodeInDev = false;
  }
  workInProgressHook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}


And again let’s simplify it:


-- CODE language-js -- function useMemo<T>(nextCreate, deps) {
  workInProgressHook = getMeMyUseMemoHook();
  const nextDeps = deps === undefined ? null : deps;
  if (workInProgressHook !== null) {
    const prevState = workInProgressHook.memoizedState;
    if (prevState !== null) {
      if (nextDeps !== null) {
        const prevDeps = prevState[1];
        if (areHookInputsEqual(nextDeps, prevDeps)) {
          return prevState[0];
        }
      }
    }
  }
  const nextValue = nextCreate();
  workInProgressHook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}

There is more going on here than with useRef but not much.

A few things to note:

  1. memoizedState earns its name here as the place a memoized value is stored. 
  2. nextCreate is a function that returns a value we want to store. 
  3. Deps is short for dependencies.


With that in mind this code does the following:

  1. If the dependencies change then call and return the value from nextCreate.
  2. If the dependencies don’t change then return the value from the last time nextCreate was called.


useMemo is basically a memoized useRef. For example, with no dependencies, the following useMemo is functionally equivalent to useRef


-- CODE language-js --const memo = useMemo(() => ({current: 8}), []);
const ref = useRef(8);


Both of the above will result in a const equal to {current:8} where the current property can be changed whenever you like.

TLDR: It's good to look at the code to demystify the magic of the unknown