Como gerar IDs exclusivos para rótulos de formulário no React?


128

Eu tenho elementos de formulário com labels e quero ter IDs exclusivos para vincular labels a elementos com htmlForatributo. Algo assim:

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

Eu costumava gerar IDs com base, this._rootNodeIDmas não está disponível desde o React 0.13. Qual é a melhor e / ou mais simples maneira de fazer isso agora?


se você estiver gerando esse elemento repetidamente, estou assumindo que em uma declaração for, por que não usar o iterador? Suponho que você também possa chamar uma função que gere um guia exclusivo se um número de índice não for bom o suficiente. stackoverflow.com/questions/105034/…
Chris Hawkes

1
Existem muitos elementos de formulário diferentes em componentes diferentes e todos eles devem ter IDs exclusivos. A função para gerar IDs é o que pensei e o que vou fazer se ninguém sugerir uma solução melhor.
Artem Sapegin

3
Você pode armazenar um contador de incremento "global" em algum lugar e usá-lo. id = 'unique' + (++GLOBAL_ID);onde var GLOBAL_ID=0;?
WiredPrairie

1
Eu sei que sou muito, muito tarde para esta festa, mas outra alternativa é envolver a entrada no rótulo em vez de usar IDs, por exemplo:<label>My label<input type="text"/></label>
Mike Desjardins

Respostas:


85

Essas soluções funcionam bem para mim.

utils/newid.js:

let lastId = 0;

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

E eu posso usá-lo assim:

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

Mas não funcionará em aplicativos isomórficos.

Adicionado 17.08.2015 . Em vez da função newId personalizada, você pode usar uniqueId do lodash.

Atualizado 28.01.2016 . É melhor gerar um ID componentWillMount.


3
Porque ele começará a gerar IDs a partir do 1º novamente no navegador. Mas, na verdade, você pode usar prefixos diferentes no servidor e no navegador.
Artem Sapegin

7
Não faça isso render! Crie o id emcomponentWillMount
sarink

1
Você criou um contêiner com estado, mas está deixando de usar setState e está violando as especificações render. facebook.github.io/react/docs/component-specs.html . Deve ser bem fácil de corrigir.
aij

3
Estou usando uniqueId da lodash no construtor e usando setState para definir o ID. Funciona bem para o meu aplicativo somente cliente.
usar o seguinte

1
componentWillMountestiver obsoleto, faça isso no construtor. Veja: reactjs.org/docs/react-component.html#unsafe_componentwillmount
Vic

78

O ID deve ser colocado dentro de componentWillMount (atualização para 2018) constructor, não render. Colocá-lo renderirá gerar novos IDs desnecessariamente.

Se você estiver usando sublinhado ou lodash, há uma uniqueIdfunção; portanto, o código resultante deve ser algo como:

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

Atualização de ganchos de 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
Ou você também pode colocá-lo no construtor.
21716 John Weisz

componentWillMount está obsoleto desde a React 16.3.0; em vez disso, use UNSAFE_componentWillMount, consulte reactjs.org/docs/react-component.html#unsafe_componentwillmount
lokers

2
Alguém pode sugerir como isso deve ser feito com os novos Hooks no React 16.8?
Aximili 6/03/19

4
Como você não está acompanhando o valor do id, você também pode usarconst {current: id} = useRef(_uniqueId('prefix-'))
forivall

1
Qual é a diferença com o uso de useRef em vez de usar State?
XPD

24

No seguimento de 04-04-2019, isso parece ser possível com o 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

Pelo que entendi, você ignora o segundo item da matriz na desestruturação da matriz que permitiria a atualização ide agora você tem um valor que não será atualizado novamente durante a vida útil do componente.

O valor de idserá myprefix-<n>onde <n>é um valor inteiro incremental retornado uniqueId. Se isso não for único o suficiente para você, considere fazer o seu próprio

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

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

ou confira a biblioteca que publiquei aqui: https://github.com/rpearce/simple-uniqueid . Existem também centenas ou milhares de outras coisas únicas de identificação por aí, mas os lodashs uniqueIdcom um prefixo devem ser suficientes para fazer o trabalho.


Atualização 2019-07-10

Agradeço ao @Huong Hk por me indicar um estado inicial lento , cuja soma é que você pode passar uma função para useStateque só seja executada na montagem inicial.

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

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

1
Tenho os mesmos problemas com a renderização do servidor, como muitos outros métodos mencionados nesta página: o componente será renderizado novamente com um novo ID no navegador.
Artem Sapegin

@ArtemSapegin: houve um problema ( github.com/facebook/react/issues/1137 ) no projeto React, discutindo ter alguma maneira de ter componentes com identificações únicas, mas acho que não aconteceu nada disso. Quão significativo é que os IDs gerados sejam os mesmos entre servidor e cliente? Eu acho que, para um <input />, o que importa é que os atributos htmlFore iddevem estar ligados, independentemente dos valores.
precisa saber é

É importante evitar atualizações DOM desnecessárias, que novos IDs causarão.
Artem Sapegin

6
É melhor se você fornecer uma função como initialState# 1 em const [ id ] = useState(() => uniqueId('myprefix-'))vez de resultado de uma função # 2 const [ id ] = useState(uniqueId('myprefix-')) O estado: idde 2 maneiras acima não serão diferentes. Mas o diferente é uniqueId('myprefix-')será executado uma vez (# 1) em vez de cada re-renderizado (# 2). Veja: preguiçoso estado inicial: reactjs.org/docs/hooks-reference.html#lazy-initial-state Como criar objetos caros preguiçosamente ?: reactjs.org/docs/...
Huong Nguyen

1
@HuongHk isso é incrível; Eu não sabia! Vou atualizar minha resposta
rpearce

4

Você pode usar uma biblioteca como o node-uuid para garantir que você obtenha IDs exclusivos.

Instale usando:

npm install node-uuid --save

Em seguida, no seu componente de reação, adicione o seguinte:

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
A resposta parece ter sido atualizada para estar em conformidade com as especificações
Jonas Berlin

2
Isso não funciona em aplicativos isomórficos, pois o ID gerado no servidor é diferente do ID gerado no cliente.
Daniel T.

2
Mas afirma-se como parte da resposta, que é muito enganador
Tom McKenzie

1
Sim, -1 por usar IDs UNIVERSALMENTE únicos, que é um martelo do tamanho de um universo para uma unha de tamanho mundial.
z

1

Espero que isso seja útil para quem procura uma solução universal / isomórfica, já que o problema da soma de verificação foi o que me levou aqui em primeiro lugar.

Como dito acima, criei um utilitário simples para criar sequencialmente um novo ID. Como os IDs continuam aumentando no servidor e iniciam novamente a partir de 0 no cliente, decidi redefinir o incremento a cada inicialização do SSR.

// utility to generate ids
let current = 0

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

export function resetIdCounter () { current = 0 }

E, em seguida, no construtor do componente raiz ou componentWillMount, chame a redefinição. Isso redefine essencialmente o escopo JS do servidor em cada renderização do servidor. No cliente, isso não (e não deve) ter nenhum efeito.


você ainda pode ter conflitos de identificação se os clientes começarem a nomear entradas de 0 novamente.
Tomasz Mularczyk

@ Tomasz, você deseja que o cliente inicie novamente o formulário 0 para que as somas de verificação correspondam.
precisa saber é o seguinte

0

Para os usos usuais de labele input, é apenas mais fácil agrupar as entradas em um rótulo como este:

import React from 'react'

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

Também possibilita nas caixas de seleção / botões de opção aplicar preenchimento ao elemento raiz e ainda obter feedback do clique na entrada.


1
1 para easyness e útil para alguns casos, não utilizável -1 com, por exemplo select, múltiplos-etiquetas em posições diferentes, componentes ui desacoplados etc., também usando ids é recomendado A11y: Em geral, as etiquetas explícitas são melhor suportados pela tecnologia de apoio, w3. org / WAI / tutoriais / formulários / etiquetas /…
Michael B.

-1

Encontrei uma solução fácil como esta:

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

Outra maneira simples com o texto datilografado:

static componentsCounter = 0;

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

2
Isso é possível sem texto datilografado
ChrisBrownie55

-1

Eu crio um módulo gerador uniqueId (Typescript):

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

export default uniqueId;

E use o módulo superior para gerar IDs exclusivos:

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

Não use IDs, se você não precisar, em vez disso, envolva a entrada em um rótulo como este:

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

Então você não precisará se preocupar com IDs únicos.


2
Embora isso seja suportado pelo HTML5, é desencorajado a acessibilidade: "Mesmo nesses casos, no entanto, é considerado uma prática recomendada definir o atributo for porque algumas tecnologias assistivas não entendem os relacionamentos implícitos entre rótulos e widgets". - from developer.mozilla.org/pt-BR/docs/Learn/HTML/Forms/…
GuyPaddock

1
Esta é a maneira recomendada pela equipe React de acordo com os documentos encontrados em reactjs.org/docs/forms.html
Blake Plumb

1
A equipe do @BlakePlumb React também possui uma seção de formulários acessíveis: reactjs.org/docs/accessibility.html#accessible-forms
Vic
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.