Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Can useMemo be used instead of useRef? #17962

Closed
svsool opened this issue Feb 3, 2020 · 3 comments
Closed

Question: Can useMemo be used instead of useRef? #17962

svsool opened this issue Feb 3, 2020 · 3 comments

Comments

@svsool
Copy link

svsool commented Feb 3, 2020

Hi, just out of curiosity can useMemo be used instead of useRef when doing it as following:

Example:

const ref = useRef(null);
const ref2 = useMemo(() => { current: null }, []);

It looks to me that both refs will be working just fine as DOM ref and as mutable value similar to instance fields in classes. Why then useRef is implemented differently comparing to useMemo considering ReactFiberHooks.js code for useRef and useMemo?

Thanks!

@bvaughn
Copy link
Contributor

bvaughn commented Feb 3, 2020

You could create a ref object this way, but it would be less efficient since useMemo also tracks and compares dependencies. (Even if you specify an empty dependency array, the underlying data structure for a memo hook has space for the extra value.)

To illustrate this, you can compare the implementation of useRef:

function mountRef<T>(initialValue: T): {|current: T|} {
const hook = mountWorkInProgressHook();
const ref = {current: initialValue};
if (__DEV__) {
Object.seal(ref);
}
hook.memoizedState = ref;
return ref;
}
function updateRef<T>(initialValue: T): {|current: T|} {
const hook = updateWorkInProgressHook();
return hook.memoizedState;
}

To useMemo:

function mountMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
function updateMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
// Assume these are defined. If they're not, areHookInputsEqual will warn.
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}

Since both hooks are used frequently, it's important they perform optimally 😄 so they are implemented different.

@yaofly2012
Copy link

You could create a ref object this way, but it would be less efficient since useMemo also tracks and compares dependencies. (Even if you specify an empty dependency array, the underlying data structure for a memo hook has space for the extra value.)

To illustrate this, you can compare the implementation of useRef:

function mountRef<T>(initialValue: T): {|current: T|} {
const hook = mountWorkInProgressHook();
const ref = {current: initialValue};
if (__DEV__) {
Object.seal(ref);
}
hook.memoizedState = ref;
return ref;
}
function updateRef<T>(initialValue: T): {|current: T|} {
const hook = updateWorkInProgressHook();
return hook.memoizedState;
}

To useMemo:

function mountMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
function updateMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
// Assume these are defined. If they're not, areHookInputsEqual will warn.
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}

Since both hooks are used frequently, it's important they perform optimally 😄 so they are implemented different.

hi, how to format the code like this ?
image

@bvaughn
Copy link
Contributor

bvaughn commented Sep 21, 2020

@yaofly2012 GitHub formats links that way, if you link to a specific file revision and line numbers.

  1. Open a file
  2. Click on a line number
  3. Hold SHIFT and click on another line number to select a range
  4. Type "y" to pin to the current revision
  5. Copy+paste the URL to GitHub

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants