如何在 React 中为表单标签生成唯一的 ID?

我有带有 label的表单元素,我希望有唯一的 ID 来将 label链接到带有 htmlFor属性的元素。就像这样:

React.createClass({
render() {
const id = ???;
return (
<label htmlFor={id}>My label</label>
<input id={id} type="text"/>
);
}
});

我过去常常基于 this._rootNodeID生成 ID,但是自从 React 0.13之后就不可用了。现在最好和/或最简单的方法是什么?

215460 次浏览

这个解决方案对我很有效。

返回文章页面

let lastId = 0;


export default function(prefix='id') {
lastId++;
return `${prefix}${lastId}`;
}

我可以这样使用它:

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"/>
);
}
});

但是在同构的应用程序中不起作用。

增加了17.08.2015

更新于28.01.2016 ,最好在 componentWillMount中生成 ID。

您可以使用类似 Node-uid的库来确保获得惟一的 id。

使用:

npm install node-uuid --save

然后在你的反应部分添加以下内容:

import {default as UUID} from "node-uuid";
import {default as React} from "react";


export default class MyComponent extends React.Component {
componentWillMount() {
this.id = UUID.v4();
},
render() {
return (
<div>
<label htmlFor={this.id}>My label</label>
<input id={this.id} type="text"/>
</div>
);
}
}

Id 应该放置在 组件 WillMount(2018年更新) constructor内,而不是 render。将其放入 render将不必要地重新生成新 id。

如果您使用下划线或 loash,那么有一个 uniqueId函数,所以您得到的代码应该类似于:

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年 Hook 更新:

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>
);
}

希望这对任何寻找通用/同构解决方案的人都有帮助,因为校验和问题是我最初来到这里的原因。

如上所述,我已经创建了一个简单的实用程序来顺序创建一个新的 id。由于服务器上的 ID 一直在递增,并且在客户机中从0开始,因此我决定重置每次 SSR 启动时的递增。

// utility to generate ids
let current = 0


export default function generateId (prefix) {
return `${prefix || 'id'}-${current++}`
}


export function resetIdCounter () { current = 0 }

然后在根组件的构造函数或组件 WillMount 中调用复位。这实际上重置了每个服务器呈现中服务器的 JS 范围。在客户端,它不会(也不应该)产生任何效果。

如果不需要,根本不要使用 ID,而是将输入包装在一个标签中,如下所示:

<label>
My Label
<input type="text"/>
</label>

那么您就不需要担心惟一的 ID 了。

在2019-04-04年,这似乎可以通过反应钩的 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

据我所知,您忽略了数组解构中允许您更新 id的第二个数组项,现在您得到了一个在组件生命周期内不会再次更新的值。

id的值将是 myprefix-<n>,其中 <n>是从 uniqueId返回的增量整数值。如果这对你来说还不够独特的话,可以考虑制作你自己喜欢的

const uniqueId = (prefix = 'id-') =>
prefix + Math.random().toString(16).slice(-4)

还有成百上千的其他唯一 ID 的东西在那里,但 loash 的 uniqueId与前缀应该足以完成这项工作。


更新2019-07-10

感谢@Huong HK 给我指出了 挂钩延迟初始状态,其总和就是你可以传递一个函数给 useState,这个函数只能在初始挂载时运行。

// before
const [ id ] = useState(uniqueId('myprefix-'))


// after
const [ id ] = useState(() => uniqueId('myprefix-'))

我找到了这样一个简单的解决办法:

class ToggleSwitch extends Component {
static id;


constructor(props) {
super(props);


if (typeof ToggleSwitch.id === 'undefined') {
ToggleSwitch.id = 0;
} else {
ToggleSwitch.id += 1;
}
this.id = ToggleSwitch.id;
}


render() {
return (
<input id={`prefix-${this.id}`} />
);
}
}

对于 labelinput的通常用法,将输入包装成这样的标签更容易:

import React from 'react'


const Field = props => (
<label>
<span>{props.label}</span>
<input type="text"/>
</label>
)

它还使得在复选框/单选按钮中向根元素应用填充成为可能,并且仍然可以获得单击输入的反馈。

我创建了一个 uniqueId 生成器模块(类型脚本) :

const uniqueId = ((): ((prefix: string) => string) => {
let counter = 0;
return (prefix: string): string => `${prefix}${++counter}`;
})();


export default uniqueId;

使用 top 模块生成唯一的 id:

import React, { FC, ReactElement } from 'react'
import uniqueId from '../../modules/uniqueId';


const Component: FC = (): ReactElement => {
const [inputId] = useState(uniqueId('input-'));
return (
<label htmlFor={inputId}>
<span>text</span>
<input id={inputId} type="text" />
</label>
);
};

不使用钩子的 Lodash 版本:

function useUniqueId() {
const [id] = useState(() => `component-${Math.random().toString(16).slice(2)}`)
return id
}

扩展 @ forivall评论

如果整个目标是连接一个 <label><input>元素,他们不依赖于道具,那么代替使用自动生成的唯一标识,最优化和性能的方法将使用 useRef

UseRef 返回一个可变的 ref 对象,其 .current属性被初始化为传递的参数(initialValue)。返回的对象将持续到组件的整个生命周期。

也就是说,你可以使用 useRef来模拟 实例变量,它不会在道具变化时重新计算。useRef不仅用于引用 DOM 元素。

使用外部随机 ID 生成器(例如 loadash)的示例

import React, { useRef } from 'react'
import uniqueId from 'lodash/utility/uniqueId'


function InputField = (props) => {
const {current: fieldId} = useRef(uniqueId('prefix-'))
return (
<div>
<input id={fieldId} type="checkbox" />
<label htmlFor={fieldId}>label</label>
</div>
);
}

使用简单的自定义随机 ID 生成器的示例

import React, { useRef } from 'react'


function InputField = (props) => {
const {current: fieldId} = useRef("prefix-" + (Math.random().toString(36)+'00000000000000000').slice(2, 7))
return (
<div>
<input id={fieldId} type="checkbox" />
<label htmlFor={fieldId}>label</label>
</div>
);
}

说明:

上面的随机 ID (Math.random().toString(36)+'00000000000000000').slice(2, 7)来自 这个堆栈溢出的答案将永远保证5个字符,而 Math.random().toString(16).slice(-4)可能返回空字符串。

另外,使用前缀 前缀必须以字母 ([A-Za-z])开头的地方以使其成为有效的 HTML4id属性值也很重要。

用过的钩子将取代不稳定的 useOpaqueIdentifier在即将到来的稳定版本的反应(已经在最新的反应阿尔法)。它将在服务器渲染和水合过程中生成稳定的 id,以避免不匹配。

更新反应18

React 18引入了一种新的钩子,它可以产生一个唯一的 ID:

const id = useId();

Hook API 文档: https://reactjs.org/docs/hooks-reference.html#useid

根据您的示例,您可以调用组件内部的钩子:

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"/>
</>
);
}