如何在 React 中为表单标签生成唯一 ID?
How to generate unique IDs for form labels in React?
我有带 label
s 的表单元素,我想有唯一的 ID 到 link 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之后就不能用了。现在最好的 and/or 最简单的方法是什么?
这个解决方案适合我。
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"/>
);
}
});
但它不适用于同构应用程序。
添加于 2015 年 8 月 17 日。您可以使用 lodash 中的 uniqueId 而不是自定义 newId 函数。
2016 年 1 月 28 日更新。生成ID最好在componentWillMount
.
您可以为此使用 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>
);
}
}
id 应该放在 componentWillMount 中(2018 年更新)constructor
,而不是 render
。把它放在 render
中会 re-generate 不必要的新 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>
);
}
希望这对前来寻找 universal/isomorphic 解决方案的人有所帮助,因为校验和问题是我首先来到这里的原因。
如上所述,我创建了一个简单的实用程序来按顺序创建一个新 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 范围。在客户端它没有(也不应该)有任何影响。
如果不需要,根本不要使用 ID,而是将输入包装在这样的标签中:
<label>
My Label
<input type="text"/>
</label>
那么您就无需担心唯一 ID。
从 2019-04-04 开始跟进,这似乎可以通过 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
返回的增量整数值。如果这对你来说不够独特,请考虑制作你自己的喜欢
const uniqueId = (prefix = 'id-') =>
prefix + Math.random().toString(16).slice(-4)
还有成百上千个其他唯一 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-'))
我找到了一个像这样的简单解决方案:
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}`} />
);
}
}
对于 label
和 input
的常用用法,将输入包装到这样的标签中会更容易:
import React from 'react'
const Field = props => (
<label>
<span>{props.label}</span>
<input type="text"/>
</label>
)
这也使得在 checkboxes/radiobuttons 中将填充应用于根元素并仍然获得点击输入的反馈成为可能。
我创建了一个 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>
);
};
没有使用 hooks 的 Lodash 的版本:
function useUniqueId() {
const [id] = useState(() => `component-${Math.random().toString(16).slice(2)}`)
return id
}
扩展@forivall's
如果整个目标是 link 增加 <label>
和 <input>
元素并且它们不依赖于 props,那么不要使用自动生成的唯一 ID,最最佳和高效的方法是使用 useRef
.
useRef returns a mutable ref object whose .current
property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
意思是,您可以使用 useRef
来模仿 instance variables,它不会在 props 更改时重新计算。 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 Whosebug answer并且将始终保证 5个字符,相比之下Math.random().toString(16).slice(-4)
可能return空字符串。
此外,使用前缀 也很重要,其中前缀必须以字母 ([A-Za-z])
开头才能成为有效的 HTML4 id
属性值。
useId 钩子将在即将发布的 React 稳定版本(已在最新的 React alphas 中可用)中取代不稳定的 useOpaqueIdentifier
。它将在服务器渲染和水合作用期间生成稳定的 ID,以避免不匹配。
更新 React 18
React 18 引入了一个生成唯一 ID 的新钩子:
const id = useId();
挂钩 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"/>
</>
);
}
我有带 label
s 的表单元素,我想有唯一的 ID 到 link 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之后就不能用了。现在最好的 and/or 最简单的方法是什么?
这个解决方案适合我。
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"/>
);
}
});
但它不适用于同构应用程序。
添加于 2015 年 8 月 17 日。您可以使用 lodash 中的 uniqueId 而不是自定义 newId 函数。
2016 年 1 月 28 日更新。生成ID最好在componentWillMount
.
您可以为此使用 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>
);
}
}
id 应该放在 componentWillMount 中(2018 年更新)constructor
,而不是 render
。把它放在 render
中会 re-generate 不必要的新 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>
);
}
希望这对前来寻找 universal/isomorphic 解决方案的人有所帮助,因为校验和问题是我首先来到这里的原因。
如上所述,我创建了一个简单的实用程序来按顺序创建一个新 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 范围。在客户端它没有(也不应该)有任何影响。
如果不需要,根本不要使用 ID,而是将输入包装在这样的标签中:
<label>
My Label
<input type="text"/>
</label>
那么您就无需担心唯一 ID。
从 2019-04-04 开始跟进,这似乎可以通过 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
返回的增量整数值。如果这对你来说不够独特,请考虑制作你自己的喜欢
const uniqueId = (prefix = 'id-') =>
prefix + Math.random().toString(16).slice(-4)
还有成百上千个其他唯一 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-'))
我找到了一个像这样的简单解决方案:
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}`} />
);
}
}
对于 label
和 input
的常用用法,将输入包装到这样的标签中会更容易:
import React from 'react'
const Field = props => (
<label>
<span>{props.label}</span>
<input type="text"/>
</label>
)
这也使得在 checkboxes/radiobuttons 中将填充应用于根元素并仍然获得点击输入的反馈成为可能。
我创建了一个 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>
);
};
没有使用 hooks 的 Lodash 的版本:
function useUniqueId() {
const [id] = useState(() => `component-${Math.random().toString(16).slice(2)}`)
return id
}
扩展@forivall's
如果整个目标是 link 增加 <label>
和 <input>
元素并且它们不依赖于 props,那么不要使用自动生成的唯一 ID,最最佳和高效的方法是使用 useRef
.
useRef returns a mutable ref object whose
.current
property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
意思是,您可以使用 useRef
来模仿 instance variables,它不会在 props 更改时重新计算。 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 Whosebug answer并且将始终保证 5个字符,相比之下Math.random().toString(16).slice(-4)
可能return空字符串。
此外,使用前缀 也很重要,其中前缀必须以字母 ([A-Za-z])
开头才能成为有效的 HTML4 id
属性值。
useId 钩子将在即将发布的 React 稳定版本(已在最新的 React alphas 中可用)中取代不稳定的 useOpaqueIdentifier
。它将在服务器渲染和水合作用期间生成稳定的 ID,以避免不匹配。
更新 React 18
React 18 引入了一个生成唯一 ID 的新钩子:
const id = useId();
挂钩 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"/>
</>
);
}