Bagaimana cara membuat ID unik untuk label formulir di React?


129

Saya memiliki elemen formulir dengan labels dan saya ingin memiliki ID unik untuk ditautkan labelke elemen dengan htmlForatribut. Sesuatu seperti ini:

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

Saya biasa membuat ID berdasarkan this._rootNodeIDtetapi tidak tersedia sejak React 0.13. Apa cara terbaik dan / atau termudah untuk melakukannya sekarang?


jika Anda membuat elemen ini berulang kali, saya berasumsi dalam pernyataan for mengapa tidak menggunakan iterator di atasnya? Saya kira Anda juga bisa memanggil fungsi yang menghasilkan panduan unik jika nomor indeks tidak cukup baik. stackoverflow.com/questions/105034/…
Chris Hawkes

1
Ada banyak elemen formulir berbeda di berbagai komponen dan semuanya harus memiliki ID unik. Fungsi untuk menghasilkan ID adalah apa yang saya pikirkan dan apa yang akan saya lakukan jika tidak ada yang menyarankan solusi yang lebih baik.
Artem Sapegin

3
Anda dapat menyimpan penghitung kenaikan "global" di suatu tempat dan menggunakannya. id = 'unique' + (++GLOBAL_ID);dimana var GLOBAL_ID=0;?
WiredPrairie

1
Saya tahu saya sangat, sangat terlambat ke pesta ini, tetapi alternatif lain adalah membungkus masukan di label alih-alih menggunakan ID, misalnya:<label>My label<input type="text"/></label>
Mike Desjardins

Jawaban:


85

Solusi ini berfungsi dengan baik untuk saya.

utils/newid.js:

let lastId = 0;

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

Dan saya bisa menggunakannya seperti ini:

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

Tetapi itu tidak akan berfungsi di aplikasi isomorfik.

Ditambahkan 17.08.2015 . Selain fungsi custom newId, Anda bisa menggunakan uniqueId dari lodash.

Diperbarui 28.01.2016 . Lebih baik membuat ID dalam componentWillMount.


3
Karena itu akan mulai menghasilkan ID dari tanggal 1 lagi di browser. Tapi sebenarnya Anda bisa menggunakan prefiks yang berbeda di server dan di browser.
Artem Sapegin

7
Jangan lakukan ini render! Buat id dicomponentWillMount
sarink

1
Anda telah membuat container stateful, tetapi mengabaikan penggunaan setState dan melanggar spesifikasi untuk render. facebook.github.io/react/docs/component-specs.html . Ini seharusnya cukup mudah untuk diperbaiki.
aij

3
Saya menggunakan uniqueId dari lodash di konstruktor dan menggunakan setState untuk menyetel id. Bekerja dengan baik untuk aplikasi khusus klien saya.
CrossProduct

1
componentWillMounttidak digunakan lagi, lakukan ini di konstruktor. Lihat: reactjs.org/docs/react-component.html#unsafe_componentwillmount
Vic

79

Id harus ditempatkan di dalam componentWillMount (update untuk 2018) constructor, bukan render. Memasukkannya renderakan menghasilkan kembali id ​​baru yang tidak perlu.

Jika Anda menggunakan garis bawah atau lodash, ada uniqueIdfungsi, jadi kode yang Anda hasilkan harus seperti:

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

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

11
Atau Anda juga bisa memasukkannya ke dalam konstruktor.
John Weisz

componentWillMount tidak digunakan lagi sejak React 16.3.0, gunakan UNSAFE_componentWillMount sebagai gantinya, lihat reactjs.org/docs/react-component.html#unsafe_componentwillmount
lokers

2
Adakah yang bisa menyarankan bagaimana hal ini harus dilakukan dengan Hooks baru di React 16.8?
Aximili

4
Karena Anda tidak melacak nilai id, Anda juga dapat menggunakanconst {current: id} = useRef(_uniqueId('prefix-'))
forivall

1
Apa perbedaan menggunakan useRef daripada use State?
XPD

24

Menindaklanjuti 2019-04-04, ini tampaknya dapat dicapai dengan 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

Seperti yang saya pahami, Anda mengabaikan item larik kedua dalam pengrusakan larik yang memungkinkan Anda memperbarui id, dan sekarang Anda memiliki nilai yang tidak akan diperbarui lagi selama masa pakai komponen.

Nilai dari idakan menjadi di myprefix-<n>mana <n>nilai integer tambahan yang dikembalikan uniqueId. Jika itu tidak cukup unik untuk Anda, pertimbangkan untuk membuat suka sendiri

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

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

atau lihat perpustakaan yang saya terbitkan dengan ini di sini: https://github.com/rpearce/simple-uniqueid . Ada juga ratusan atau ribuan ID unik lainnya di luar sana, tetapi lodash uniqueIddengan awalan sudah cukup untuk menyelesaikan pekerjaan.


Perbarui 2019-07-10

Terima kasih kepada @Huong Hk karena telah mengarahkan saya ke status awal malas hooks , yang jumlahnya adalah Anda dapat meneruskan fungsi useStateyang hanya akan dijalankan pada pemasangan awal.

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

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

1
Saya memiliki masalah yang sama dengan rendering server, seperti banyak metode lain, yang disebutkan di halaman ini: komponen akan merender dengan ID baru di browser.
Artem Sapegin

@ArtemSapegin: ada masalah ( github.com/facebook/react/issues/1137 ) pada proyek React yang membahas tentang cara agar komponen memiliki id unik, tetapi saya rasa tidak ada hasil. Seberapa penting ID yang dihasilkan sama antara server dan klien? Saya akan berpikir bahwa untuk seorang <input />, yang penting adalah bahwa atribut htmlFordan idharus diikat bersama apa pun nilainya.
rpearce

Sangat penting untuk menghindari pembaruan DOM yang tidak perlu, yang akan menyebabkan ID baru.
Artem Sapegin

6
Lebih baik jika Anda memberikan fungsi sebagai initialState# 1 const [ id ] = useState(() => uniqueId('myprefix-'))daripada hasil fungsi # 2 const [ id ] = useState(uniqueId('myprefix-')) Status: iddari 2 cara di atas tidak berbeda. Tapi perbedaannya uniqueId('myprefix-')akan dieksekusi sekali (# 1) daripada setiap dirender ulang (# 2). Lihat: Kondisi awal malas: reactjs.org/docs/hooks-reference.html#lazy-initial-state Cara membuat objek mahal dengan malas ?: reactjs.org/docs/…
Huong Nguyen

1
@HuongHk itu luar biasa; Saya tidak tahu! Saya akan memperbarui jawaban saya
rpearce

4

Anda bisa menggunakan pustaka seperti node-uuid untuk ini untuk memastikan Anda mendapatkan id unik.

Instal menggunakan:

npm install node-uuid --save

Kemudian di komponen react Anda, tambahkan yang berikut ini:

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
Jawabannya tampaknya telah diperbarui untuk memenuhi spesifikasi
Jonas Berlin

2
Ini tidak berfungsi di aplikasi isomorfik, karena id yang dihasilkan di server berbeda dengan id yang dihasilkan di klien.
Daniel T.

2
Tetapi ini dinyatakan sebagai bagian dari jawaban, yang sangat menyesatkan
Tom McKenzie

1
Ya, -1 untuk menggunakan ID unik UNIVERSAL, itu adalah palu seukuran alam semesta untuk paku seukuran dunia.
Jon z

1

Semoga ini bermanfaat bagi siapa saja yang datang mencari solusi universal / isomorfik, karena masalah checksum yang membawa saya ke sini.

Seperti yang dikatakan di atas, saya telah membuat utilitas sederhana untuk membuat id baru secara berurutan. Karena ID terus bertambah di server, dan mulai dari 0 di klien, saya memutuskan untuk mengatur ulang kenaikan setiap SSR dimulai.

// utility to generate ids
let current = 0

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

export function resetIdCounter () { current = 0 }

Dan kemudian di konstruktor komponen root atau componentWillMount, panggil reset. Ini pada dasarnya menyetel ulang cakupan JS untuk server di setiap render server. Di klien itu tidak (dan seharusnya) tidak berpengaruh.


Anda mungkin masih mengalami bentrokan id jika klien mulai menamai input dari 0 lagi.
Tomasz Mularczyk

@Tomasz Anda ingin klien memulai kembali dari bentuk 0 sehingga checksum cocok.
tenor528

0

Untuk penggunaan biasa dari labeldan input, akan lebih mudah untuk memasukkan input ke dalam label seperti ini:

import React from 'react'

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

Ini juga memungkinkan di kotak centang / radiobuttons untuk menerapkan padding ke elemen root dan masih mendapatkan umpan balik dari klik pada masukan.


1
+1 untuk kemudahan dan berguna untuk beberapa kasus, -1 tidak dapat digunakan dengan misalnya select, banyak label pada posisi yang berbeda, komponen ui yang tidak digabungkan, dll., Juga menggunakan id disarankan a11y: Umumnya, label eksplisit lebih baik didukung oleh teknologi pendukung, w3. org / WAI / tutorials / forms / labels /…
Michael B.

-1

Saya menemukan solusi mudah seperti ini:

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

Cara sederhana lainnya dengan ketikan:

static componentsCounter = 0;

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

2
Ini dimungkinkan tanpa TypeScript
ChrisBrownie55

-1

Saya membuat modul generator uniqueId (Typecript):

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

export default uniqueId;

Dan gunakan modul atas untuk menghasilkan id unik:

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

Jangan gunakan ID sama sekali jika Anda tidak perlu, alih-alih bungkus input dalam label seperti ini:

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

Maka Anda tidak perlu khawatir tentang ID unik.


2
Meskipun ini didukung oleh HTML5, namun tidak disarankan untuk aksesibilitas: "Bahkan dalam kasus seperti itu, dianggap praktik terbaik untuk menyetel atribut for karena beberapa teknologi pendukung tidak memahami hubungan implisit antara label dan widget." - dari developer.mozilla.org/en-US/docs/Learn/HTML/Forms/…
GuyPaddock

1
Ini adalah cara yang direkomendasikan oleh tim React menurut dokumen yang ditemukan di reactjs.org/docs/forms.html
Blake Plumb

1
Tim @BlakePlumb React juga memiliki bagian formulir yang dapat diakses: reactjs.org/docs/accessibility.html#accessible-forms
Vic
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.