How to generate unique IDs for form labels in React?
The id should be placed inside of componentWillMount (update for 2018) constructor
, not render
. Putting it in render
will re-generate new ids unnecessarily.
If you're using underscore or lodash, there is a uniqueId
function, so your resulting code should be something like:
constructor(props) {
super(props);
this.id = _.uniqueId("prefix-");
}
render() {
const id = this.id;
return (
<div>
<input id={id} type="checkbox" />
<label htmlFor={id}>label</label>
</div>
);
}
2019 Hooks update:
import React, { useState } from 'react';
import _uniqueId from 'lodash/uniqueId';
const MyComponent = (props) => {
// id will be set once when the component initially renders, but never again
// (unless you assigned and called the second argument of the tuple)
const [id] = useState(_uniqueId('prefix-'));
return (
<div>
<input id={id} type="checkbox" />
<label htmlFor={id}>label</label>
</div>
);
}
Following up as of 2019-04-04, this seems to be able to be accomplished with the React Hooks' useState
:
import React, { useState } from 'react'
import uniqueId from 'lodash/utility/uniqueId'
const Field = props => {
const [ id ] = useState(uniqueId('myprefix-'))
return (
<div>
<label htmlFor={id}>{props.label}</label>
<input id={id} type="text"/>
</div>
)
}
export default Field
As I understand it, you ignore the second array item in the array destructuring that would allow you to update id
, and now you've got a value that won't be updated again for the life of the component.
The value of id
will be myprefix-<n>
where <n>
is an incremental integer value returned from uniqueId
. If that's not unique enough for you, consider making your own like
const uniqueId = (prefix = 'id-') =>
prefix + Math.random().toString(16).slice(-4)
There are also hundreds or thousands of other unique ID things out there, but lodash's uniqueId
with a prefix should be enough to get the job done.
Update 2019-07-10
Thanks to @Huong Hk for pointing me to hooks lazy initial state, the sum of which is that you can pass a function to useState
that will only be run on the initial mount.
// before
const [ id ] = useState(uniqueId('myprefix-'))
// after
const [ id ] = useState(() => uniqueId('myprefix-'))
Update React 18
React 18 has introduced a new hook which generates a unique ID:
const id = useId();
Hook API docs: https://reactjs.org/docs/hooks-reference.html#useid
From your example, you could call the hook inside a component:
import React, { useId } from 'react'
function TextField = (props) => {
// generate unique ID
const id = useId();
return (
<>
<label htmlFor={id}>My label</label>
<input id={id} type="text"/>
</>
);
}
This solutions works fine for me.
utils/newid.js
:
let lastId = 0;
export default function(prefix='id') {
lastId++;
return `${prefix}${lastId}`;
}
And I can use it like this:
import newId from '../utils/newid';
React.createClass({
componentWillMount() {
this.id = newId();
},
render() {
return (
<label htmlFor={this.id}>My label</label>
<input id={this.id} type="text"/>
);
}
});
But it won’t work in isomorphic apps.
Added 17.08.2015. Instead of custom newId function you can use uniqueId from lodash.
Updated 28.01.2016. It’s better to generate ID in componentWillMount
.