Wie generiere ich eindeutige IDs für Formularbeschriftungen in React?

128

Ich habe Formularelemente mit labels und ich möchte eindeutige IDs haben, um labels mit Elementen mit htmlForAttribut zu verknüpfen . Etwas wie das:

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

Früher habe ich IDs basierend auf generiert, this._rootNodeIDaber seit React 0.13 ist es nicht mehr verfügbar. Was ist der beste und / oder einfachste Weg, dies jetzt zu tun?

Artem Sapegin
quelle
Wenn Sie dieses Element immer wieder generieren, gehe ich in einer for-Anweisung davon aus, warum Sie nicht den Iterator darauf verwenden. Ich nehme an, Sie könnten auch eine Funktion aufrufen, die eine eindeutige Guid generiert, wenn eine Indexnummer nicht gut genug ist. stackoverflow.com/questions/105034/…
Chris Hawkes
1
Es gibt viele verschiedene Formularelemente in verschiedenen Komponenten, und alle sollten eindeutige IDs haben. Die Funktion zum Generieren von IDs ist das, worüber ich nachgedacht habe und was ich tun werde, wenn niemand eine bessere Lösung vorschlägt.
Artem Sapegin
3
Sie können irgendwo einen "globalen" Inkrementierungszähler speichern und diesen verwenden. id = 'unique' + (++GLOBAL_ID);wo var GLOBAL_ID=0;?
WiredPrairie
1
Ich weiß, dass ich sehr, sehr spät zu dieser Party komme, aber eine andere Alternative besteht darin, die Eingabe in das Etikett einzuschließen, anstatt IDs zu verwenden, z. B.:<label>My label<input type="text"/></label>
Mike Desjardins

Antworten:

85

Diese Lösung funktioniert gut für mich.

utils/newid.js::

let lastId = 0;

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

Und ich kann es so benutzen:

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

In isomorphen Apps funktioniert dies jedoch nicht.

Hinzugefügt am 17.08.2015 . Anstelle der benutzerdefinierten Funktion newId können Sie uniqueId von lodash verwenden.

Aktualisiert am 28.01.2016 . Es ist besser, eine ID in zu generieren componentWillMount.

Artem Sapegin
quelle
3
Weil es ab dem 1. im Browser wieder IDs generiert. Tatsächlich können Sie jedoch verschiedene Präfixe auf dem Server und im Browser verwenden.
Artem Sapegin
7
Mach das nicht in render! Erstellen Sie die ID incomponentWillMount
Sarink
1
Sie haben einen Stateful-Container erstellt, vernachlässigen jedoch die Verwendung von setState und verletzen die Spezifikation für render. facebook.github.io/react/docs/component-specs.html . Es sollte jedoch ziemlich einfach zu beheben sein.
aij
3
Ich verwende uniqueId von lodash im Konstruktor und setze setState, um die ID festzulegen. Funktioniert gut für meine Client-App.
CrossProduct
1
componentWillMountist veraltet, tun Sie dies stattdessen im Konstruktor. Siehe: reactjs.org/docs/react-component.html#unsafe_componentwillmount
Vic
78

Die ID sollte in componentWillMount (Update für 2018) platziert werden constructor, nicht render. Wenn Sie es rendereingeben, werden unnötigerweise neue IDs neu generiert.

Wenn Sie Unterstrich oder lodash verwenden, gibt es eine uniqueIdFunktion, sodass der resultierende Code ungefähr so aussehen sollte:

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 Update:

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>
  );
}
Sarink
quelle
11
Oder Sie können es auch in den Konstruktor einfügen.
John Weisz
componentWillMount ist seit React 16.3.0 veraltet. Verwenden Sie stattdessen UNSAFE_componentWillMount, siehe reactjs.org/docs/react-component.html#unsafe_componentwillmount
lokers
2
Kann jemand vorschlagen, wie dies mit den neuen Hooks in React 16.8 gemacht werden soll?
Aximili
4
Da Sie den Wert der ID nicht verfolgen, können Sie auchconst {current: id} = useRef(_uniqueId('prefix-'))
forivall
1
Was ist der Unterschied zur Verwendung von useRef anstelle von use State?
XPD
24

Nach dem Stand vom 04.04.2019 scheint dies mit den React Hooks erreicht werden zu können 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

Soweit ich weiß, ignorieren Sie das zweite Array-Element in der Array-Destrukturierung, mit dem Sie aktualisieren können id, und jetzt haben Sie einen Wert, der während der gesamten Lebensdauer der Komponente nicht erneut aktualisiert wird.

Der Wert von idwird dort sein, myprefix-<n>wo <n>ein inkrementeller ganzzahliger Wert zurückgegeben wird uniqueId. Wenn das für Sie nicht einzigartig genug ist, sollten Sie überlegen, wie Sie es sich wünschen

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

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

oder sehen Sie sich die Bibliothek an, die ich hier veröffentlicht habe: https://github.com/rpearce/simple-uniqueid . Es gibt auch Hunderte oder Tausende anderer eindeutiger ID-Dinge, aber Lodashs uniqueIdmit einem Präfix sollten ausreichen, um die Arbeit zu erledigen.


Update 2019-07-10

Vielen Dank an @Huong Hk, der mich auf den faulen Anfangszustand von Hooks hingewiesen hat Die Summe davon ist, dass Sie eine Funktion übergeben können useState, die nur auf dem anfänglichen Mount ausgeführt wird.

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

// after
const [ id ] = useState(() => uniqueId('myprefix-'))
rpearce
quelle
1
Ich habe die gleichen Probleme beim Rendern von Servern wie viele andere auf dieser Seite erwähnte Methoden: Die Komponente wird mit einer neuen ID im Browser erneut gerendert.
Artem Sapegin
@ArtemSapegin: Es gab ein Problem ( github.com/facebook/react/issues/1137 ) im React-Projekt, bei dem es darum ging, Komponenten mit eindeutigen IDs zu versehen, aber ich glaube, es ist nichts daraus geworden. Wie wichtig ist es, dass die generierten IDs zwischen Server und Client gleich sind? Ich würde denken, dass für ein <input />, was wichtig wäre, dass die htmlForund idAttribute zusammengebunden werden sollten, egal was die Werte sind.
rpearce
Es ist wichtig, unnötige DOM-Updates zu vermeiden, die durch neue IDs verursacht werden.
Artem Sapegin
6
Es ist besser, wenn Sie eine Funktion als initialState# 1 const [ id ] = useState(() => uniqueId('myprefix-'))anstelle des Ergebnisses einer Funktion # 2 bereitstellen. const [ id ] = useState(uniqueId('myprefix-')) Der Status: idvon 2 oben genannten Möglichkeiten ist nicht unterschiedlich. Aber das andere uniqueId('myprefix-')wird einmal ausgeführt (# 1) anstatt jedes neu gerenderte (# 2). Siehe: Fauler Ausgangszustand: reactjs.org/docs/hooks-reference.html#lazy-initial-state Wie man teure Objekte träge erstellt?: Reactjs.org/docs/…
Huong Nguyen
1
@ HuongHk das ist erstaunlich; Ich wusste es nicht! Ich werde meine Antwort aktualisieren
erscheint
4

Sie können eine Bibliothek wie node-uuid verwenden , um sicherzustellen, dass Sie eindeutige IDs erhalten.

Installieren Sie mit:

npm install node-uuid --save

Fügen Sie dann in Ihrer Reaktionskomponente Folgendes hinzu:

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>
    );
  }   
}
Stuart Ervine
quelle
3
Unrein renderverstößt gegen facebook.github.io/react/docs/component-specs.html
aij
2
Die Antwort scheint aktualisiert worden zu sein, um der Spezifikation zu entsprechen
Jonas Berlin
2
Dies funktioniert in isomorphen Apps nicht, da sich die auf dem Server generierte ID von der auf dem Client generierten ID unterscheidet.
Daniel T.
2
Aber es wird als Teil der Antwort angegeben, die sehr irreführend ist
Tom McKenzie
1
Ya, -1 für die Verwendung von UNIVERSAL eindeutigen IDs, das ist ein Hammer in Universumsgröße für einen Nagel in Weltgröße.
Jon z
1

Hoffentlich ist dies hilfreich für alle, die nach einer universellen / isomorphen Lösung suchen, da mich das Problem der Prüfsumme in erster Linie hierher geführt hat.

Wie oben erwähnt, habe ich ein einfaches Dienstprogramm erstellt, um nacheinander eine neue ID zu erstellen. Da die IDs auf dem Server weiter inkrementiert werden und im Client von 0 neu beginnen, habe ich beschlossen, das Inkrement bei jedem Start der SSR zurückzusetzen.

// utility to generate ids
let current = 0

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

export function resetIdCounter () { current = 0 }

Rufen Sie dann im Konstruktor oder in der Komponente WillMount der Stammkomponente den Reset auf. Dadurch wird der JS-Bereich für den Server in jedem Server-Rendering im Wesentlichen zurückgesetzt. Im Client hat (und sollte) es keine Wirkung.

Tenor528
quelle
Es kann immer noch zu ID-Konflikten kommen, wenn Clients erneut Eingaben von 0 benennen.
Tomasz Mularczyk
@Tomasz Sie möchten, dass der Client erneut mit Formular 0 beginnt, damit die Prüfsummen übereinstimmen.
Tenor528
0

Für die üblichen Verwendungen von labelund inputist es einfach einfacher, Eingaben in ein Etikett wie dieses zu verpacken:

import React from 'react'

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

Es ist auch möglich, in Kontrollkästchen / Radiobuttons das Root-Element aufzufüllen und dennoch Feedback zum Klicken auf die Eingabe zu erhalten.

Entwicklung
quelle
1
+1 zur Vereinfachung und in einigen Fällen nützlich, -1 nicht verwendbar mit z. B. selectMehrfachbeschriftungen an verschiedenen Positionen, entkoppelten UI-Komponenten usw., auch unter Verwendung von IDs wird empfohlen a11y: Im Allgemeinen werden explizite Beschriftungen besser von der Hilfstechnologie w3 unterstützt. org / WAI / Tutorials / Formulare / Labels /…
Michael B.
-1

Ich habe eine einfache Lösung wie diese gefunden:

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

Andere einfache Möglichkeit mit Typoskript:

static componentsCounter = 0;

componentDidMount() {
  this.setState({ id: 'input-' + Input.componentsCounter++ });
}
Lucas Moyano Angelini
quelle
2
Dies ist ohne TypeScript möglich
ChrisBrownie55
-1

Ich erstelle ein uniqueId-Generatormodul (Typescript):

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

export default uniqueId;

Verwenden Sie das Top-Modul, um eindeutige IDs zu generieren:

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>
  );
};     
Masih Jahangiri
quelle
-3

Verwenden Sie keine IDs, wenn Sie dies nicht benötigen, sondern verpacken Sie die Eingabe in ein Etikett wie das folgende:

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

Dann müssen Sie sich keine Gedanken mehr über eindeutige IDs machen.

Mike Desjardins
quelle
2
Obwohl dies von HTML5 unterstützt wird, wird von der Barrierefreiheit abgeraten: "Selbst in solchen Fällen wird es als bewährte Methode angesehen, das for-Attribut festzulegen, da einige unterstützende Technologien implizite Beziehungen zwischen Labels und Widgets nicht verstehen." - von developer.mozilla.org/en-US/docs/Learn/HTML/Forms/…
GuyPaddock
1
Dies ist die vom React-Team empfohlene Methode gemäß den Dokumenten unter reactjs.org/docs/forms.html
Blake Plumb,
1
@BlakePlumb React Team hat auch einen zugänglichen Formularbereich: reactjs.org/docs/accessibility.html#accessible-forms
Vic