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


128

我有带有labels的表单元素,并且我希望有唯一的ID将labels 链接到具有htmlForattribute的元素。像这样:

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

我曾经根据生成ID,this._rootNodeID但自从React 0.13起就不可用了。现在最好和/或最简单的方法是什么?


如果您要一遍又一遍地生成此元素,我假设在for语句中为什么不对它使用迭代器?我想您也可以调用一个函数,如果索引号不够好,该函数会生成唯一的guid。stackoverflow.com/questions/105034/…–
克里斯·霍克斯

1
在不同的组件中有许多不同的表单元素,并且所有组件都应具有唯一的ID。生成ID的功能是我的想法,如果没有人提出更好的解决方案,我将要做的事情。
Artem Sapegin

3
您可以将“全局”递增计数器存储在某个位置并使用它。 id = 'unique' + (++GLOBAL_ID);var GLOBAL_ID=0;
WiredPrairie 2015年

1
我知道我参加这个聚会非常非常晚,但是另一种选择是将输入内容包装在标签中而不是使用ID,例如:<label>My label<input type="text"/></label>
Mike Desjardins

Answers:


85

这个解决方案对我来说很好。

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。相反,自定义NEWID的功能,您可以使用UNIQUEID从lodash。

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


3
因为它将再次从浏览器的第一个开始生成ID。但是实际上您可以在服务器和浏览器中使用不同的前缀。
Artem Sapegin 2015年

7
不要这样做render!在componentWillMount
sarink中

1
您已经建立了一个有状态的容器,但忽略了使用setState并违反的规范renderfacebook.github.io/react/docs/component-specs.html。它应该很容易修复。
aij 2016年

3
我在构造函数中使用lodash中的uniqueId,并使用setState设置ID。适用于我的仅客户端应用程序。
CrossProduct

1
componentWillMount不推荐使用,而是在构造函数中执行此操作。请参阅:reactjs.org/docs/react-component.html#unsafe_componentwillmount
维克

78

该ID应该放置在componentWillMount(更新为2018)内部constructor,而不是render。放入它render会不必要地重新生成新的ID。

如果使用下划线或破折号,则有一个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挂钩更新:

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

11
或者,您也可以将其放入构造函数中。
John Weisz

componentWillMount被废弃了,因为阵营16.3.0,使用UNSAFE_componentWillMount而是看reactjs.org/docs/react-component.html#unsafe_componentwillmount
lokers

2
谁能建议在React 16.8中使用新的Hooks来做到这一点?
Aximili

4
由于您没有跟踪ID的值,因此您也可以使用const {current: id} = useRef(_uniqueId('prefix-'))
forivall

1
使用useRef代替use State有什么区别?
XPD

24

截至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东西,但是uniqueId带前缀的lodash 足以完成任务。


更新2019-07-10

感谢@Huong Hk为我指出了钩子惰性初始状态,其总和是您可以将一个函数传递给该函数,useState该函数将仅在初始安装上运行。

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

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

1
与本页上提到的许多其他方法一样,我在服务器渲染方面也遇到了同样的问题:该组件将在浏览器中使用新的ID重新渲染。
Artem Sapegin

@ArtemSapegin:在React项目上有一个问题(github.com/facebook/react/issues/1137),讨论的是让组件具有唯一ID的某种方法,但是我不认为有什么问题。服务器和客户端之间生成的ID相同有多重要?我认为对于而言<input />,重要的是,无论值是多少,都应将htmlForand id属性绑在一起。
rpearce

避免新ID会引起不必要的DOM更新非常重要。
Artem Sapegin

6
如果将函数提供为initialState#1 const [ id ] = useState(() => uniqueId('myprefix-'))而不是函数#2的结果,则更好。上面两种方式const [ id ] = useState(uniqueId('myprefix-')) 的state:id没什么不同。但是不同的是uniqueId('myprefix-')将执行一次(#1),而不是每次重新渲染(#2)。请参阅:懒惰的初始状态:reactjs.org/docs/hooks-reference.html#lazy-initial-state如何懒洋洋昂贵创建对象?:reactjs.org/docs/...
梅家阮

1
@HuongHk太神奇了;我不知道!我将更新我的答案
rpearce

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


2
答案似乎已经过更新,符合规范
Jonas Berlin

2
这在同构应用中不起作用,因为服务器上生成的ID与客户端上生成的ID不同。
Daniel T.

2
但这只是答案的一部分,这很容易引起误解
Tom McKenzie

1
Ya,-1,表示使用UNIVERSALLY唯一ID,这是用于世界大小指甲的宇宙大小锤子。
Jon z

1

希望这对任何寻求通用/同构解决方案的人都有帮助,因为首先导致校验和问题的原因是我。

如上所述,我已经创建了一个简单的实用程序来顺序创建新的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 }

然后在根组件的构造函数或componentWillMount中,调用reset。这实际上是在每个服务器渲染中重置服务器的JS范围。在客户端中它没有(也不应该)有任何作用。


如果客户端再次从0开始命名输入,您可能仍会发生ID冲突。
Tomasz Mularczyk

@Tomasz,您希望客户端重新从表格0开始,以便校验和匹配。
tenor528

0

对于通常的惯例labelinput,它只是更容易包装投入这样的标签:

import React from 'react'

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

这也使得可以在复选框/单选按钮中将填充应用于根元素,并且仍然获得单击输入的反馈。


1
为方便起见,+ 1是有用的,并且在某些情况下很有用;建议不要使用-1,例如select,在不同位置使用多个标签,未耦合的ui组件等,还应使用id来使用a11y:通常,辅助技术w3
Michael B.

-1

我找到了一个简单的解决方案,如下所示:

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


-1

我创建一个uniqueId生成器模块(Typescript):

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

export default uniqueId;

并使用顶层模块生成唯一的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>
  );
};     

-3

如果不需要,请不要使用ID,而是将输入内容包装在这样的标签中:

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

这样,您就不必担心唯一ID。


2
尽管HTML5支持此功能,但不建议使用辅助功能:“即使在这种情况下,由于某些辅助技术无法理解标签和小部件之间的隐式关系,因此,设置for属性也被视为最佳实践。” -从developer.mozilla.org/en-US/docs/Learn/HTML/Forms/...
GuyPaddock

1
这是React团队根据在reactjs.org/docs/forms.html上
Blake Plumb

1
@BlakePlumb React团队还有一个可访问的表单部分:reactjs.org/docs/accessibility.html#accessible-forms
Vic
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.