neeraj080
neeraj080

Reputation: 129

How would you implement useCallback using useState from scratch?

This is how I have implemented it. Does this look ok?

function useCallback(fn, deps) {
    const [state, setState] = useState({ fn });

    useEffect(() => {
        setState({ fn });
    }, deps);

    return state.fn;
}

Upvotes: 2

Views: 1450

Answers (1)

hackape
hackape

Reputation: 19957

Nice try but not perfect. Your implementation calls setState() which triggers another round of re-render of component, that doesn’t match up to the behavior of the real useCallback() hook.

OK challenge accepted.

Actually useState() isn’t a good building block for implementing useCallback(), useRef() is better. But to meet your requirement, lemme first write useRef() with useState().

function useRef(value) {
  const [ref] = useState({ current: value })
  ref.current = value
  return ref
}

Easy piecy. We don’t need useEffect(). All we want is just a function to compare the deps between re-render and see if they change.

function depsChanged(deps1, deps2) {
  if (deps1 === undefined || deps2 === undefined) return true
  if (deps1.length !== deps2.length) return true
  for (let i in deps1) {
    if (!Object.is(deps1[i], deps2[i])) return true
  }
  return false
}

Now we can implement useCallback()

function useCallback(fn, deps) {
  const slots = useRef([fn, deps]).current
  if (depsChanged(slots[1], deps)) {
    slots[0] = fn
  }
  slots[1] = deps
  return slots[0]
}

Upvotes: 7

Related Questions