Làm cách nào để tạo ID duy nhất cho nhãn biểu mẫu trong React?


128

Tôi có các phần tử biểu mẫu với labels và tôi muốn có ID duy nhất để liên kết labels với các phần tử có htmlForthuộc tính. Một cái gì đó như thế này:

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

Tôi đã sử dụng để tạo ID dựa trên this._rootNodeIDnhưng nó không khả dụng kể từ React 0.13. Cách tốt nhất và / hoặc đơn giản nhất để làm điều đó bây giờ là gì?


nếu bạn tạo phần tử này nhiều lần, tôi giả sử trong một câu lệnh for tại sao không sử dụng trình lặp trên nó? Tôi cho rằng bạn cũng có thể gọi một hàm tạo ra một hướng dẫn duy nhất nếu số chỉ mục không đủ tốt. stackoverflow.com/questions/105034/ Mạnh
Chris Hawkes

1
Có nhiều yếu tố hình thức khác nhau trong các thành phần khác nhau và tất cả chúng nên có ID duy nhất. Chức năng tạo ID là những gì tôi nghĩ và tôi sẽ làm gì nếu không ai đề xuất giải pháp tốt hơn.
Artem Sapegin

3
Bạn có thể lưu trữ một bộ đếm tăng "toàn cầu" ở đâu đó và sử dụng nó. id = 'unique' + (++GLOBAL_ID);var GLOBAL_ID=0;đâu
WiredPrairi

1
Tôi biết tôi rất, rất muộn cho bữa tiệc này, nhưng một cách khác là bọc đầu vào trong nhãn thay vì sử dụng ID, ví dụ:<label>My label<input type="text"/></label>
Mike Desjardins

Câu trả lời:


85

Giải pháp này hoạt động tốt cho tôi.

utils/newid.js:

let lastId = 0;

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

Và tôi có thể sử dụng nó như thế này:

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

Nhưng nó sẽ không hoạt động trong các ứng dụng đẳng hình.

Đã thêm 17.08.2015 . Thay vì chức năng newId tùy chỉnh, bạn có thể sử dụng uniqueId từ lodash.

Cập nhật ngày 28.01.2016 . Tốt hơn là tạo ID trong componentWillMount.


3
Bởi vì nó sẽ bắt đầu tạo ID từ lần đầu tiên trong trình duyệt. Nhưng thực tế bạn có thể sử dụng các tiền tố khác nhau trên máy chủ và trong trình duyệt.
Artem Sapegin

7
Đừng làm điều này trong render! Tạo id trongcomponentWillMount
sarink

1
Bạn đã tạo một thùng chứa trạng thái, nhưng đang bỏ qua việc sử dụng setState và đang vi phạm thông số kỹ thuật cho render. facebook.github.io/react/docs/component-specs.html . Nó sẽ khá dễ dàng để sửa chữa mặc dù.
aij

3
Tôi đang sử dụng uniqueId từ lodash trong hàm tạo và sử dụng setState để đặt id. Hoạt động tốt cho ứng dụng chỉ khách hàng của tôi.
Sản phẩm chéo

1
componentWillMountkhông được dùng nữa, hãy làm điều này trong constructor thay thế. Xem: Reacjs.org/docs/react-component.html#unsafe_componentwillmount
Vic

78

Id nên được đặt bên trong thành phầnWillMount (cập nhật cho năm 2018) constructor, không render. Đưa nó vào rendersẽ tạo lại id mới một cách không cần thiết.

Nếu bạn đang sử dụng dấu gạch dưới hoặc dấu gạch ngang, có một uniqueIdhàm, vì vậy mã kết quả của bạn sẽ giống như:

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

Cập nhật móc 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
Hoặc bạn cũng có thể đặt nó vào hàm tạo.
John Weisz

thành phầnWillMount không được chấp nhận kể từ React 16.3.0, thay vào đó, hãy sử dụng UNSAFE_componentWillMount, xem Reacjs.org/docs/react-component.html#unsafe_componentwillmount
vay

2
Bất cứ ai cũng có thể đề nghị làm thế nào điều này nên được thực hiện với các Móc mới trong React 16.8?
Aimumili

4
Vì bạn không theo dõi giá trị của id, bạn cũng có thể sử dụngconst {current: id} = useRef(_uniqueId('prefix-'))
trước

1
Sự khác biệt khi sử dụng useRef thay vì sử dụng State là gì?
XPD

24

Theo sau kể từ 2019-04-04, điều này dường như có thể được thực hiện với React Hook ' 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

Theo tôi hiểu, bạn bỏ qua mục mảng thứ hai trong việc hủy mảng sẽ cho phép bạn cập nhật id và bây giờ bạn đã có một giá trị sẽ không được cập nhật lại cho vòng đời của thành phần.

Giá trị của idsẽ là myprefix-<n>nơi <n>giá trị nguyên tăng dần được trả về uniqueId. Nếu điều đó không đủ độc đáo cho bạn, hãy cân nhắc việc tự làm

function gen4() {
  return Math.random().toString(16).slice(-4)
}

function simpleUniqueId(prefix) {
  return (prefix || '').concat([
    gen4(),
    gen4(),
    gen4(),
    gen4(),
    gen4(),
    gen4(),
    gen4(),
    gen4()
  ].join(''))
}

hoặc kiểm tra thư viện tôi đã xuất bản với điều này tại đây: https://github.com/rpearce/simple-uniqueid . Ngoài ra còn có hàng trăm hoặc hàng ngàn thứ ID duy nhất khác ngoài đó, nhưng những từ uniqueIdcó tiền tố là đủ để hoàn thành công việc.


Cập nhật 2019-07-10

Cảm ơn @Huong Hk đã chỉ cho tôi móc trạng thái ban đầu lười biếng , tổng số đó là bạn có thể truyền một hàm cho useStatenó sẽ chỉ được chạy trên mount ban đầu.

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

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

1
Tôi có cùng một vấn đề với kết xuất máy chủ, như nhiều phương pháp khác, được đề cập trên trang này: thành phần sẽ đăng ký lại với một ID mới trong trình duyệt.
Artem Sapegin

@ArtemSapegin: có một vấn đề ( github.com/facebook/react/issues/1137 ) về dự án React thảo luận về việc có một số cách để có các thành phần có id duy nhất, nhưng tôi không nghĩ có gì xảy ra. Tầm quan trọng của việc các ID được tạo giống nhau giữa máy chủ và máy khách như thế nào? Tôi nghĩ rằng đối với một <input />, điều quan trọng là các thuộc tính htmlForidcác thuộc tính phải được gắn với nhau cho dù giá trị là gì.
rpearce

Điều quan trọng là tránh các cập nhật DOM không cần thiết, ID mới sẽ gây ra.
Artem Sapegin

6
Sẽ tốt hơn nếu bạn cung cấp một hàm là initialStatesố 1 const [ id ] = useState(() => uniqueId('myprefix-'))thay vì kết quả của hàm số 2 const [ id ] = useState(uniqueId('myprefix-')) Trạng thái: idcủa 2 cách trên không khác nhau. Nhưng khác nhau uniqueId('myprefix-')sẽ được thực hiện một lần (# 1) thay vì mỗi lần kết xuất lại (# 2). Xem: Trạng thái ban đầu lười biếng: Reacjs.org/docs/hooks-reference.html#lazy-initial-state Làm thế nào để tạo ra những đồ vật đắt tiền một cách lười biếng?: Reacjs.org/docs/iêu
Hương Nguyễn

1
@HuongHk thật tuyệt vời; Tôi không biết! Tôi sẽ cập nhật câu trả lời của mình
rpearce 11/07/19

4

Bạn có thể sử dụng một thư viện như nút-uuid cho việc này để đảm bảo bạn có được các id duy nhất.

Cài đặt bằng:

npm install node-uuid --save

Sau đó, trong thành phần phản ứng của bạn thêm vào như sau:

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
Câu trả lời dường như đã được cập nhật để tuân thủ thông số kỹ thuật
Jonas Berlin

2
Điều này không hoạt động trong các ứng dụng đẳng cấu, vì id được tạo trên máy chủ khác với id tạo trên máy khách.
Daniel T.

2
Nhưng nó được nêu là một phần của câu trả lời, rất sai lệch
Tom McKenzie

1
Ya, -1 vì đã sử dụng id độc đáo KHÔNG GIỚI HẠN, đó là một cây búa có kích thước vũ trụ cho móng tay cỡ thế giới.
Jon z

1

Hy vọng rằng điều này hữu ích cho bất kỳ ai đang tìm kiếm một giải pháp phổ quát / đẳng cấu, vì vấn đề tổng kiểm tra là nguyên nhân dẫn tôi đến đây ngay từ đầu.

Như đã nói ở trên, tôi đã tạo một tiện ích đơn giản để tuần tự tạo một id mới. Vì ID liên tục tăng trên máy chủ và bắt đầu lại từ 0 trong máy khách, tôi quyết định đặt lại mức tăng mỗi SSR bắt đầu.

// utility to generate ids
let current = 0

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

export function resetIdCounter () { current = 0 }

Và sau đó, trong hàm tạo của thành phần gốc hoặc thành phầnWillMount, hãy gọi thiết lập lại. Điều này về cơ bản đặt lại phạm vi JS cho máy chủ trong mỗi máy chủ kết xuất. Trong máy khách, nó không (và không nên) có bất kỳ ảnh hưởng nào.


bạn vẫn có thể có xung đột id nếu khách hàng bắt đầu đặt tên đầu vào từ 0 lần nữa.
Tomasz Mularchot

@Tomasz bạn muốn khách hàng bắt đầu lại mẫu 0 để tổng kiểm khớp với nhau.
tenor528

0

Đối với các cách sử dụng thông thường của labelinput, thật dễ dàng hơn để bọc đầu vào vào một nhãn như thế này:

import React from 'react'

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

Nó cũng cho phép các hộp kiểm / radiobutton có thể áp dụng phần đệm cho phần tử gốc và vẫn nhận được phản hồi khi nhấp vào đầu vào.


1
1 cho easyness và hữu ích cho một số trường hợp, -1 không sử dụng được với ví dụ select, nhiều nhãn vào các vị trí khác nhau, các thành phần giao diện người dùng đồng đều vv, cũng sử dụng id được khuyến khích a11y: Nói chung, nhãn rõ ràng được hỗ trợ tốt hơn bằng công nghệ hỗ trợ, w3. org / WAI / hướng dẫn / hình thức / nhãn / Hoài
Michael B.

-1

Tôi tìm thấy một giải pháp dễ dàng như thế này:

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

Cách đơn giản khác với bản thảo:

static componentsCounter = 0;

componentDidMount() {
  this.setState({ id: 'input-' + Input.componentsCounter++ });
}

2
Điều này là có thể mà không cần
TypeScript

-1

Tôi tạo một mô-đun trình tạo uniqueId (Bản mô tả):

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

export default uniqueId;

Và sử dụng mô-đun hàng đầu để tạo id duy nhất:

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

Hoàn toàn không sử dụng ID nếu bạn không cần, thay vào đó hãy bọc đầu vào trong một nhãn như thế này:

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

Sau đó, bạn sẽ không cần phải lo lắng về ID duy nhất.


2
Mặc dù điều này được HTML5 hỗ trợ, nhưng nó không khuyến khích khả năng truy cập: "Tuy nhiên, trong những trường hợp như vậy, nó được coi là cách tốt nhất để đặt thuộc tính vì một số công nghệ hỗ trợ không hiểu mối quan hệ ngầm giữa nhãn và widget." - từ developer.mozilla.org/en-US/docs/Learn/HTML/Forms/,
GuyPaddock

1
Đây là cách được nhóm React khuyên dùng theo các tài liệu được tìm thấy tại Reacjs.org/docs/forms.html
Blake Plumb

1
Nhóm @BlakePlumb React cũng có phần biểu mẫu có thể truy cập: Reacjs.org/docs/accessibility.html#accessible-forms
Vic
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.