Skip to content

Instantly share code, notes, and snippets.

@sqren sqren/useComponentId.js
Last active Feb 26, 2020

Embed
What would you like to do?
React hook for getting a unique identifier for a component
import { useRef } from 'react';
let uniqueId = 0;
const getUniqueId = () => uniqueId++;
export function useComponentId() {
const idRef = useRef(getUniqueId());
return idRef.current;
}
@akomm

This comment has been minimized.

Copy link

akomm commented Jan 17, 2020

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.

@akomm

This comment has been minimized.

Copy link

akomm commented Jan 17, 2020

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];
}
@macabeus

This comment has been minimized.

Copy link

macabeus commented Feb 22, 2020

@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 foo++ and ++foo, but I think that this is a rare case that it could be useful.
Otherwise:

useMemo(() => (idCounter += 1, idCounter), [prefix])
useMemo(
  () => {
    idCounter += 1
    return idCounter
  },
  [prefix]
)
@akomm

This comment has been minimized.

Copy link

akomm commented Feb 25, 2020

@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?

@macabeus

This comment has been minimized.

Copy link

macabeus commented Feb 25, 2020

@AKMM As long I understand, useMemo is called just when the requirements is changed. So, in this expression:

useMemo(() => idCounter++, [prefix])

the idCounter will be increments only when prefix is changed, not every time like you said.

And, yes, this code is simpler. Using just 7 lines you can do everything that the 20 lines does.
And it uses fewer browser's features, such as localStorage.

useMemo fixes the problem that you said about useRef.

@TrevorBurnham

This comment has been minimized.

Copy link

TrevorBurnham commented Feb 25, 2020

Going by Dan Abramov's comment here, it sounds like the useRef implementation was on the right track. You can avoid calling getUniqueId() on every render by using a conditional on idRef.current:

let uniqueId = 0;
const getUniqueId = () => uniqueId++;

export function useComponentId() {
  const idRef = useRef(null);
  if (idRef.current === null) {
    idRef.current = getUniqueId()
  }
  return idRef.current;
}
@macabeus

This comment has been minimized.

Copy link

macabeus commented Feb 25, 2020

But using useRef for everything else that isn't a component is strange, at least for me.
Is more semantic and clear just using useMemo instead of useRef and on new line if (ref.current === null) {, that is almost the same of useMemo.

@akomm

This comment has been minimized.

Copy link

akomm commented Feb 26, 2020

@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
Thanks for the link. With the fix, it does make sense. I wanted to either point out an error in the gist (which it is as it seems) or get new insight in something I might not knew there. Got it via side-channel from you :). Conclusion: I guess useMemo is more generic and does argument iteration to check against the dependencies, so checking manually with an simple weak null check and useRef is more efficient. Makes sense :)

@macabeus

This comment has been minimized.

Copy link

macabeus commented Feb 26, 2020

@akomm Okay, now I understand your point. Thank you for reply =]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.