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

Posted

技术标签:

【中文标题】如何在 React 中为表单标签生成唯一 ID?【英文标题】:How to generate unique IDs for form labels in React? 【发布时间】:2015-06-07 20:57:46 【问题描述】:

我有带有labels 的表单元素,我希望有唯一的ID 将labels 链接到带有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,例如:&lt;label&gt;My label&lt;input type="text"/&gt;&lt;/label&gt; 【参考方案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-&lt;n&gt;,其中&lt;n&gt; 是从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 相同的重要性有多大?我认为对于&lt;input /&gt;,重要的是htmlForid 属性应该绑定在一起,无论值是什么。 避免不必要的 DOM 更新很重要,因为新的 ID 会导致。 最好提供一个函数为initialState #1 const [ id ] = useState(() =&gt; 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

如果整个目标是链接 &lt;label&gt;&lt;input&gt; 元素并且它们不依赖于道具,那么最好不要使用自动生成的唯一 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】:

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

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

如何在我的 schema.graphql 文件中为按 id 过滤的查询引用生成的 Prisma 模式

如何生成 4 位唯一 id,如 discord id,如 #8052