import { useRef } from 'react'; | |
let uniqueId = 0; | |
const getUniqueId = () => uniqueId++; | |
export function useComponentId() { | |
const idRef = useRef(getUniqueId()); | |
return idRef.current; | |
} |
This comment has been minimized.
This comment has been minimized.
Used it for this btw. Thanks for the inspiration: import {useCallback, useMemo, useState} from "react";
let id = 0;
const getKey = () => id++;
export function useSessionState<TState>(initialState: TState): [TState, (state: TState) => void] {
const key = `__session_state__${useMemo(getKey, [])}`;
const storedState = sessionStorage.getItem(key);
if (storedState) {
try {
initialState = JSON.parse(storedState);
} catch (e) {
console.error(e);
}
}
const [state, setState] = useState(initialState);
const storeState = useCallback((state: TState) => {
setState(state);
sessionStorage.setItem(key, JSON.stringify(state));
}, [key]);
return [state, storeState];
} |
This comment has been minimized.
This comment has been minimized.
@AKomn I think that this approach is simpler and enough: import { useMemo } from 'react'
let idCounter = 0
const useUniqueId = (prefix: string): string => {
const id = useMemo(() => idCounter++, [prefix])
return `${prefix}${id}`
}
export default useUniqueId I was also inspired by lodash code. I really hate the unaries operators useMemo(() => (idCounter += 1, idCounter), [prefix]) useMemo(
() => {
idCounter += 1
return idCounter
},
[prefix]
) |
This comment has been minimized.
This comment has been minimized.
@macabeus I am not quite getting. You just posted the same thing, only creating the callback for no reason every time the function is called, saying its simpler? |
This comment has been minimized.
This comment has been minimized.
@AKMM As long I understand, useMemo(() => idCounter++, [prefix]) the And, yes, this code is simpler. Using just 7 lines you can do everything that the 20 lines does.
|
This comment has been minimized.
This comment has been minimized.
Going by Dan Abramov's comment here, it sounds like the let uniqueId = 0;
const getUniqueId = () => uniqueId++;
export function useComponentId() {
const idRef = useRef(null);
if (idRef.current === null) {
idRef.current = getUniqueId()
}
return idRef.current;
} |
This comment has been minimized.
This comment has been minimized.
But using |
This comment has been minimized.
This comment has been minimized.
@macabeus I think you did not understand my initial post. I posted a use case plus counter and you posted counter, telling its simpler. It does not make any sense. When I subtract the use case from my initial post, then you have posted the same counter I did, except you create the arrow function each time, while I don't - minor disadvantage, but no benefits, so what is the point. The creation of the arrow function has nothing to do with the arrow function being called once in this specific example. Its two different questions. @TrevorBurnham |
This comment has been minimized.
This comment has been minimized.
@akomm Okay, now I understand your point. Thank you for reply =] |
This comment has been minimized.
This comment has been minimized.
Hey, There is an alternative implementation with let uniqueId = 0;
export function useComponentId() {
const [componentId] = useState(() => uniqueId++);
return componentId;
} |
This comment has been minimized.
This comment has been minimized.
@mbelsky You can do that, but your example will increment the ID on each render call, even thought no new ID is required. You can fix it making the Also see: https://twitter.com/dan_abramov/status/1099842565631819776?s=20 regarding useRef & useState |
This comment has been minimized.
This comment has been minimized.
@akomm I've tested my solution with this demo: https://codesandbox.io/s/determined-goldstine-qj761 A component rerenders there on click event and uniqueId stays same. How should I change this demo to see behavior that you described? |
This comment has been minimized.
This comment has been minimized.
@mbelsky in your example, its the |
This comment has been minimized.
This comment has been minimized.
@akomm could you explain please what the difference between them and how I can reproduce the issue that you described above?
I'm still not understanding what wrong with this solution |
This comment has been minimized.
This comment has been minimized.
just log the |
This comment has been minimized.
This comment has been minimized.
@akomm got it, thanks. I've updated originally post & demo. btw there is another interesting issue: |
This comment has been minimized.
This comment has been minimized.
Your proposal worked. Its just a question if you want to increment the id forever on each render or not. The solution would be, as I proposed above, to replace |
This comment has been minimized.
This comment has been minimized.
codesandbox is flaky there, hence your odd numbers. Try it locally, it should work. I used to use codepen, then codesandbox came out and was better. Liked it. But recently it got more and more flaky. A pure bugfeast. Might be there bundler configuration or life reload that messes it up. IF you do some tests, you notice on life reload its rendered twice. |
This comment has been minimized.
This comment has been minimized.
I'll try, thank you! |
This comment has been minimized.
This comment has been minimized.
For anyone landing here using Typescript, it is generally advised to avoid the use of So here's a slight adaptation: let uniqueId = 0;
const getUniqueId = () => uniqueId++;
export function useComponentId() {
const idRef = useRef<number>();
if (idRef.current === undefined) {
idRef.current = getUniqueId();
}
return idRef.current;
} |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
The export function useComponentId() {
const id = useRef(getUniqueId());
return id.current;
} (edited, forgot |
This comment has been minimized.
This comment has been minimized.
@notthatnathan it has actually been explained here. |
This comment has been minimized.
This comment has been minimized.
Oops, missed it. Thanks. |
This comment has been minimized.
This comment has been minimized.
FWIW, after generating the id (I used Also, for test snapshot support, you'll want a predictable id, maybe using a different prop from the instance.
|
This comment has been minimized.
This comment has been minimized.
Why is it not needed and how do the two things relate (I don't know your shortid implementation).
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. |
This comment has been minimized.
This comment has been minimized.
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.
I'm not sure I understand the question. I'm just saying that this:
and this
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. ( updated, typo in the second code example |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
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;
} |
This comment has been minimized.
Interesting idea.
Is there a reason why you use ref? Because
getUniqueId
will be called on every component render/update, that uses the hook.I
useMemo
for that, since its callback only called once.