Come generare ID univoci per le etichette dei moduli in React?


128

Ho elementi del modulo con labels e voglio avere ID univoci per collegare labels agli elementi con htmlForattributo. Qualcosa come questo:

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

Generavo ID basati su this._rootNodeIDma non è disponibile da React 0.13. Qual è il modo migliore e / o più semplice per farlo ora?


se stai generando questo elemento più e più volte, presumo in un'istruzione for perché non usare l'iteratore su di esso? Suppongo che potresti anche chiamare una funzione che genera una guida univoca se un numero indice non è abbastanza buono. stackoverflow.com/questions/105034/…
Chris Hawkes

1
Esistono molti diversi elementi del modulo in diversi componenti e tutti dovrebbero avere ID univoci. La funzione per generare ID è ciò a cui ho pensato e che cosa farò se nessuno suggerisce una soluzione migliore.
Artem Sapegin,

3
È possibile memorizzare un contatore incrementale "globale" da qualche parte e utilizzarlo. id = 'unique' + (++GLOBAL_ID);dove var GLOBAL_ID=0;?
WiredPrairie,

1
So di essere molto, molto in ritardo per questa festa, ma un'altra alternativa è quella di avvolgere l'input nell'etichetta invece di usare gli ID, ad esempio:<label>My label<input type="text"/></label>
Mike Desjardins,

Risposte:


85

Questa soluzione funziona bene per me.

utils/newid.js:

let lastId = 0;

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

E posso usarlo in questo modo:

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

Ma non funzionerà nelle app isomorfe.

Aggiunto il 17.08.2015 . Invece della funzione newId personalizzata puoi usare uniqueId da lodash.

Aggiornato il 28.01.2016 . È meglio generare ID in componentWillMount.


3
Perché inizierà a generare nuovamente gli ID dal primo nel browser. Ma in realtà è possibile utilizzare prefissi diversi sul server e nel browser.
Artem Sapegin,

7
Non farlo dentro render! Crea l'id incomponentWillMount
sarink

1
Hai creato un contenitore con stato, ma stai trascurando di usare setState e stai violando le specifiche per render. facebook.github.io/react/docs/component-specs.html . Dovrebbe essere abbastanza facile da risolvere però.
aij,

3
Sto usando uniqueId di lodash nel costruttore e sto usando setState per impostare l'id. Funziona bene con l'app solo per il mio cliente.
CrossProduct,

1
componentWillMountè deprecato, fallo invece nel costruttore. Vedi: reactionjs.org/docs/react-component.html#unsafe_componentwillmount
Vic

78

L'id deve essere inserito all'interno di componentWillMount (aggiornamento per il 2018) constructor, non render. Mettendolo dentrorender rigenererai inutilmente nuovi ID.

Se stai usando il trattino basso o lodash, c'è una uniqueIdfunzione, quindi il tuo codice risultante dovrebbe essere simile a:

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

Aggiornamento ganci 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
Oppure potresti anche inserirlo nel costruttore.
John Weisz,

componentWillMount è deprecato da React 16.3.0, usa invece UNSAFE_componentWillMount, vedi reazionijs.org/docs/react-component.html#unsafe_componentwillmount
lokers

2
Qualcuno può suggerire come dovrebbe essere fatto con i nuovi Hooks in React 16.8?
Aximili

4
Poiché non stai monitorando il valore dell'ID, puoi anche utilizzareconst {current: id} = useRef(_uniqueId('prefix-'))
1919

1
Qual è la differenza con l'utilizzo di useRef invece di use State?
XPD

24

A partire dal 04-04-2019, questo sembra poter essere realizzato con i 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

A quanto ho capito, ignori il secondo elemento dell'array nella distruzione dell'array che ti consentirebbe l'aggiornamento id, e ora hai un valore che non verrà aggiornato di nuovo per la vita del componente.

Il valore di idsarà myprefix-<n>dove <n>è un valore intero incrementale restituito uniqueId. Se non è abbastanza unico per te, prendi in considerazione l'idea di crearne uno tuo

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

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

oppure dai un'occhiata alla libreria che ho pubblicato qui: https://github.com/rpearce/simple-uniqueid . Ci sono anche centinaia o migliaia di altri ID univoci là fuori, ma lodash uniqueIdcon un prefisso dovrebbe essere sufficiente per portare a termine il lavoro.


Aggiornamento del 10-07-2019

Grazie a @Huong Hk per avermi indicato lo stato iniziale pigro degli hook , la cui somma è che è possibile passare una funzione useStateche verrà eseguita solo sul mount iniziale.

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

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

1
Ho gli stessi problemi con il rendering del server, come molti altri metodi, menzionati in questa pagina: il componente eseguirà nuovamente il rendering con un nuovo ID nel browser.
Artem Sapegin,

@ArtemSapegin: c'è stato un problema ( github.com/facebook/react/issues/1137 ) sul progetto React che parlava di avere un modo per avere componenti con ID univoci, ma non credo che ne sia venuto fuori nulla. Quanto è significativo che gli ID generati siano gli stessi tra server e client? Penserei che per un <input />, ciò che conta è che gli attributi htmlFore iddovrebbero essere legati insieme, indipendentemente dai valori.
rpearce,

È significativo evitare inutili aggiornamenti DOM che causeranno nuovi ID.
Artem Sapegin,

6
È meglio se si fornisce una funzione come initialState# 1 const [ id ] = useState(() => uniqueId('myprefix-'))invece del risultato di una funzione # 2 const [ id ] = useState(uniqueId('myprefix-')) Lo stato: iddi 2 modi sopra non sono diversi. Ma il diverso uniqueId('myprefix-')verrà eseguito una volta (n. 1) invece di ogni nuovo rendering (n. 2). Vedi: stato iniziale pigro: reazionejs.org/docs/hooks-reference.html#lazy-initial-state Come creare pigramente oggetti costosi ?: reajs.org/docs/…
Huong Nguyen

1
@HuongHk è fantastico; Non lo sapevo! Aggiornerò la mia risposta
rpearce l'

4

È possibile utilizzare una libreria come node-uuid per assicurarsi di ottenere ID univoci.

Installa usando:

npm install node-uuid --save

Quindi nel componente di reazione aggiungere quanto segue:

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
La risposta sembra essere stata aggiornata per soddisfare le specifiche
Jonas Berlin,

2
Questo non funziona nelle app isomorfe, poiché l'id generato sul server è diverso da quello generato sul client.
Daniel T.

2
Ma è indicato come parte della risposta, che è molto fuorviante
Tom McKenzie,

1
Sì, -1 per l'utilizzo di ID unici UNIVERSALMENTE, questo è un martello di dimensioni universali per un chiodo di dimensioni mondiali.
Jon z

1

Spero che questo sia utile a chiunque stia cercando una soluzione universale / isomorfa, dal momento che la questione del checksum è ciò che mi ha portato qui in primo luogo.

Come detto sopra, ho creato una semplice utility per creare in sequenza un nuovo ID. Poiché gli ID continuano ad aumentare sul server e ricominciare da 0 nel client, ho deciso di reimpostare l'incremento a ogni avvio dell'SSR.

// utility to generate ids
let current = 0

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

export function resetIdCounter () { current = 0 }

E quindi nel costruttore del componente root o componentWillMount, chiama il reset. Questo essenzialmente reimposta l'ambito JS per il server in ciascun rendering del server. Nel client non ha (e non dovrebbe) avere alcun effetto.


potresti avere ancora conflitti di identificazione se i client iniziano a nominare nuovamente gli input da 0.
Tomasz Mularczyk,

@Tomasz vuoi che il client ricomincia dal modulo 0 in modo che i checksum corrispondano.
tenor528

0

Per i soliti usi di labele input, è più semplice avvolgere l'input in un'etichetta come questa:

import React from 'react'

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

Inoltre, nelle caselle di controllo / pulsanti radio è possibile applicare il riempimento all'elemento radice e ottenere comunque il feedback del clic sull'input.


1
+1 per semplicità e utile in alcuni casi, -1 non utilizzabile ad es select. Con più etichette su posizioni diverse, componenti ui non accoppiati ecc., Anche l'uso di ID è consigliato a11y: in genere, le etichette esplicite sono meglio supportate dalla tecnologia assistiva, w3. org / WAI / tutorial / moduli / etichette /…
Michael B.

-1

Ho trovato una soluzione semplice come questa:

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

Altro modo semplice con dattiloscritto:

static componentsCounter = 0;

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

2
Questo è possibile senza TypeScript
ChrisBrownie55,

-1

Creo un modulo generatore uniqueId (Typescript):

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

export default uniqueId;

E usa il modulo superiore per generare ID univoci:

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

Non utilizzare affatto ID se non è necessario, ma avvolgere l'input in un'etichetta come questa:

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

Quindi non dovrai preoccuparti di ID univoci.


2
Sebbene sia supportato da HTML5, è sconsigliato per l'accessibilità: "Anche in questi casi, tuttavia, è considerata la migliore pratica impostare l'attributo for perché alcune tecnologie assistive non comprendono le relazioni implicite tra etichette e widget". - da developer.mozilla.org/en-US/docs/Learn/HTML/Forms/…
GuyPaddock

1
Questo è il modo raccomandato dal team di React in base ai documenti trovati su reazionijs.org/docs/forms.html
Blake Plumb

1
Il team di @BlakePlumb React ha anche una sezione di moduli accessibili: reazionijs.org/docs/accessibility.html#accessible-forms
Vic
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.