-
-
Save sorenlouv/fc897c1629979e669714893df966b1b7 to your computer and use it in GitHub Desktop.
import { useRef } from 'react'; | |
let uniqueId = 0; | |
const getUniqueId = () => uniqueId++; | |
export function useComponentId() { | |
const idRef = useRef(getUniqueId()); | |
return idRef.current; | |
} |
Using this with Strict Mode will give you a different value on the 2nd render. I assume this is okay to use otherwise, and it's just some quirk of Strict Mode that produces differing values? I've used it in production w/o apparent issue...
Edit: looks like it's the result of having a harmless side effect: facebook/react#20826
Trying to move away from this Hook in order to be StrictMode compatible (despite it working 100% fine otherwise)... wish there was a React.useComponentName
Hook...
The useRef
version works in my testing, but why not just set the initial value rather than checking undefined/null and assigning? Am I missing something here?
export function useComponentId() {
const id = useRef(getUniqueId());
return id.current;
}
(edited, forgot .current
in the return)
@notthatnathan it has actually been explained here.
Oops, missed it. Thanks.
FWIW, after generating the id (I used shortid()
) as the initial value (as mentioned above), it doesn't change in my testing. I don't believe the undefined
check and reassignment is necessary with refs.
Also, for test snapshot support, you'll want a predictable id, maybe using a different prop from the instance.
const id = useRef(process.env.NODE_ENV === 'test' ? `test-${title}` : shortid());
FWIW, after generating the id (I used
shortid()
) as the initial value (as mentioned above), it doesn't change in my testing. I don't believe theundefined
check and reassignment is necessary with refs.
Why is it not needed and how do the two things relate (I don't know your shortid implementation).
Also, for test snapshot support, you'll want a predictable id, maybe using a different prop from the instance.
const id = useRef(process.env.NODE_ENV === 'test' ? `test-${title}` : shortid());
You add test-related code in a component? And also you change behavior depending on how id is used the outcome might be different in test than prod.
You add test-related code in a component? And also you change behavior depending on how id is used the outcome might be different in test than prod.
Doesn't change behavior, just changes the id. Otherwise every run of your test, you get snapshot updates (new id on mount), which defeats the purpose of snapshots.
Why is it not needed and how do the two things relate (I don't know your shortid implementation).
I'm not sure I understand the question. I'm just saying that this:
const idRef = useRef(null);
if (idRef.current === null) {
idRef.current = getUniqueId()
}
return idRef.current;
and this
const idRef = useRef(getUniqueId());
return idRef.current;
both return a unique ID that doesn't change on re-render. Which makes sense, refs wouldn't be useful if the initial value changed. Log from the calling component to see what I mean.
(shortid
is just an id-generating package my org uses)
updated, typo in the second code example
You add test-related code in a component? And also you change behavior depending on how id is used the outcome might be different in test than prod.
Doesn't change behavior, just changes the id. Otherwise every run of your test, you get snapshot updates (new id on mount), which defeats the purpose of snapshots.
It does. You use different ID generation methods. If one of the different methods generate non-unique ID, it will work in one, but fail in the other or lead to different results, depending on how you use the generated ID.
Sorry. I don't get what you are talking about. You've just changed things replying to my initial question. Why is it null instead of undefined? Also you did not answer why the undefined check is not needed. I don't understand the problem.
In cases where I don't need globally unique IDs, but rather IDs unique per component I'm using this hook:
export function useIdGenerator(): () => number {
const ref = useRef(0);
function getId() {
ref.current += 1;
return ref.current;
}
return getId;
}
On a side note, nanoId
is considerably faster than shortId
or uniqueId
according to their benchmarks.
There is an open issue for this on the react repo.
Here is a more robust implementation that also supports SSR.
Also, for test snapshot support, you'll want a predictable id, maybe using a different prop from the instance.
You shouldn't be using snapshots at all. write proper tests.
For anyone stumbling upon this in 2023 there's React.useId
for this very purpose since React v18.
For anyone landing here using Typescript, it is generally advised to avoid the use of
null
. It simplifies types, and once you try to stop using it you realize you really don't need it 95% of the time because the JS already gives youundefined
to work with.So here's a slight adaptation: