Reputation: 129
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
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