如何在 React 中为表单标签生成唯一 ID?
Posted
技术标签:
【中文标题】如何在 React 中为表单标签生成唯一 ID?【英文标题】:How to generate unique IDs for form labels in React? 【发布时间】:2015-06-07 20:57:46 【问题描述】:我有带有label
s 的表单元素,我希望有唯一的ID 将label
s 链接到带有htmlFor
属性的元素。像这样的:
React.createClass(
render()
const id = ???;
return (
<label htmlFor=id>My label</label>
<input id=id type="text"/>
);
);
我曾经根据this._rootNodeID
生成 ID,但从 React 0.13 开始它不可用。现在最好和/或最简单的方法是什么?
【问题讨论】:
如果你一遍又一遍地生成这个元素,我假设在 for 语句中为什么不使用迭代器呢?我想如果索引号不够好,您还可以调用一个生成唯一 guid 的函数。 ***.com/questions/105034/… 在不同的组件中有很多不同的表单元素,它们都应该有唯一的ID。生成 ID 的功能是我想到的,如果没有人提出更好的解决方案,我会做什么。 您可以在某处存储一个“全局”递增计数器并使用它。id = 'unique' + (++GLOBAL_ID);
var GLOBAL_ID=0;
?
我知道我参加这个聚会已经非常非常晚了,但另一种选择是将输入包装在标签中而不是使用 ID,例如:<label>My label<input type="text"/></label>
【参考方案1】:
id 应该放在 componentWillMount 内(2018 年更新)constructor
,而不是 render
。将其放入 render
将不必要地重新生成新的 ID。
如果您使用的是下划线或 lodash,则有一个 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 年 Hooks 更新:
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>
);
【讨论】:
或者你也可以把它放到构造函数中。 谁能建议如何使用 React 16.8 中的新 Hooks 来完成这项工作? 由于没有跟踪id的值,所以也可以使用const current: id = useRef(_uniqueId('prefix-'))
用useRef代替useState有什么区别?
不要将状态用于不是由于给定组件的更改(不变)而导致的。概念上是错误的。【参考方案2】:
这个解决方案对我来说很好。
utils/newid.js
:
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。您可以使用来自 lodash 的 uniqueId 来代替自定义 newId 函数。
2016 年 1 月 28 日更新。最好在componentWillMount
生成ID。
【讨论】:
因为它会在浏览器中再次从第一个开始生成 ID。但实际上你可以在服务器和浏览器中使用不同的前缀。 你找到了同构应用的解决方案吗?由于客户端和服务器上的 id 不同,我得到校验和失败。 不要在render
中这样做!在componentWillMount
中创建id
你已经创建了一个有状态的容器,但是忽略了使用 setState 并且违反了 render
的规范。 facebook.github.io/react/docs/component-specs.html 。不过应该很容易修复。
我在构造函数中使用来自 lodash 的 uniqueId 并使用 setState 来设置 id。适用于我的仅限客户端的应用程序。【参考方案3】:
从 2019 年 4 月 4 日开始,这似乎可以通过 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
据我了解,您忽略了数组解构中允许您更新id
的第二个数组项,现在您获得了一个在组件生命周期内不会再次更新的值。
id
的值将是myprefix-<n>
,其中<n>
是从uniqueId
返回的增量整数值。如果这对您来说还不够独特,请考虑制作自己的喜欢
function gen4()
return Math.random().toString(16).slice(-4)
function simpleUniqueId(prefix)
return (prefix || '').concat([
gen4(),
gen4(),
gen4(),
gen4(),
gen4(),
gen4(),
gen4(),
gen4()
].join(''))
或查看我在这里发布的库:https://github.com/rpearce/simple-uniqueid。还有成百上千个其他唯一 ID 事物,但 lodash 的 uniqueId
带有前缀应该足以完成工作。
2019-07-10 更新
感谢@Huong Hk 将我指向hooks lazy initial state,总而言之,您可以将一个函数传递给useState
,该函数只会在初始挂载时运行。
// before
const [ id ] = useState(uniqueId('myprefix-'))
// after
const [ id ] = useState(() => uniqueId('myprefix-'))
【讨论】:
我在服务器渲染方面遇到了同样的问题,就像本页提到的许多其他方法一样:组件将在浏览器中使用新 ID 重新渲染。 @ArtemSapegin:React 项目中存在一个问题 (github.com/facebook/react/issues/1137),讨论了如何让组件具有唯一 ID,但我认为没有任何结果。服务器和客户端生成的 ID 相同的重要性有多大?我认为对于<input />
,重要的是htmlFor
和id
属性应该绑定在一起,无论值是什么。
避免不必要的 DOM 更新很重要,因为新的 ID 会导致。
最好提供一个函数为initialState
#1 const [ id ] = useState(() => uniqueId('myprefix-'))
而不是函数的结果#2 const [ id ] = useState(uniqueId('myprefix-'))
状态:id
以上两种方式没有区别。但不同的是uniqueId('myprefix-')
将执行一次(#1),而不是每次重新渲染(#2)。参见:懒惰初始状态:reactjs.org/docs/hooks-reference.html#lazy-initial-state如何懒惰地创建昂贵的对象?:reactjs.org/docs/…
@HuongHk 太棒了;我不知道!我会更新我的答案【参考方案4】:
您可以为此使用诸如 node-uuid 之类的库来确保您获得唯一的 ID。
安装使用:
npm install node-uuid --save
然后在你的 react 组件中添加以下内容:
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>
);
【讨论】:
不纯的render
违反facebook.github.io/react/docs/component-specs.html
答案似乎已经更新以符合规范
这在同构应用程序中不起作用,因为服务器上生成的 id 与客户端上生成的 id 不同。
但它被说成是答案的一部分,这是非常具有误导性的
“ID 太独特”似乎是投反对票的任意理由。 UUID 并不昂贵。【参考方案5】:
希望这对任何寻求通用/同构解决方案的人有所帮助,因为校验和问题是导致我首先来到这里的原因。
如上所述,我创建了一个简单的实用程序来顺序创建一个新的 id。由于 ID 在服务器上不断增加,并且在客户端从 0 开始,因此我决定在 s-s-r 启动时重置增量。
// utility to generate ids
let current = 0
export default function generateId (prefix)
return `$prefix || 'id'-$current++`
export function resetIdCounter () current = 0
然后在根组件的构造函数或componentWillMount中,调用reset。这实质上会在每个服务器渲染中重置服务器的 JS 范围。在客户端它没有(也不应该)有任何影响。
【讨论】:
如果客户端再次从 0 开始命名输入,您仍然可能会遇到 id 冲突。 @Tomasz 您希望客户端从表单 0 重新开始,以便校验和匹配。【参考方案6】:使用钩子的没有 Lodash 的版本:
function useUniqueId()
const [id] = useState(() => `component-$Math.random().toString(16).slice(2)`)
return id
【讨论】:
【参考方案7】:扩展@forivall的comment
如果整个目标是链接 <label>
和 <input>
元素并且它们不依赖于道具,那么最好不要使用自动生成的唯一 ID,而最好和性能最好的方法是使用 @987654323 @。
useRef 返回一个可变的 ref 对象,其
.current
属性被初始化为传递的参数(initialValue)。返回的对象将在组件的整个生命周期内持续存在。
意思是,您可以使用useRef
来模仿instance variables,它不会在道具更改时重新计算。 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)
来自 this *** answer,将始终保证 5 个字符,而 Math.random().toString(16).slice(-4)
可能返回空字符串。
此外,使用前缀很重要前缀必须以字母([A-Za-z])
开头,这样它才能成为有效的HTML4 id
属性值。
【讨论】:
【参考方案8】:我创建了一个 uniqueId 生成器模块(Typescript):
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>
);
;
【讨论】:
【参考方案9】:对于label
和input
的常用用法,将输入包装到这样的标签中更容易:
import React from 'react'
const Field = props => (
<label>
<span>props.label</span>
<input type="text"/>
</label>
)
它还可以在复选框/单选按钮中将填充应用于根元素并仍然获得点击输入的反馈。
【讨论】:
+1 表示方便且在某些情况下有用,-1 不适用于例如select
,不同位置的多标签,不耦合的ui组件等,也推荐使用id a11y:一般辅助技术支持显式标签更好,w3.org/WAI/tutorials/forms/labels/…【参考方案10】:
我找到了这样一个简单的解决方案:
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` />
);
【讨论】:
【参考方案11】:如果不需要,请不要使用 ID,而是将输入包装在这样的标签中:
<label>
My Label
<input type="text"/>
</label>
那么您就不必担心唯一 ID。
【讨论】:
虽然 HTML5 支持此功能,但不鼓励使用可访问性:“即使在这种情况下,设置 for 属性也被认为是最佳实践,因为一些辅助技术不理解标签和小部件之间的隐式关系。” -- 来自developer.mozilla.org/en-US/docs/Learn/HTML/Forms/… 这是 React 团队根据 reactjs.org/docs/forms.html 找到的文档推荐的方式 @BlakePlumb React 团队也有一个可访问的表单部分:reactjs.org/docs/accessibility.html#accessible-forms以上是关于如何在 React 中为表单标签生成唯一 ID?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 CodeIgniter 中为 is_unique 表单验证启用查询生成器?
在 PL-SQL 存储过程中为 XML 中的每个段生成唯一 ID
如何在 SQL 中自动生成唯一 ID,如 UID12345678?
如何在 Android 应用程序中为联系我们表单设置 onClickListener