Як генерувати унікальні ідентифікатори для міток форми в React?


128

У мене є елементи форми з labels, і я хочу мати унікальні ідентифікатори для зв’язку labels до елементів з htmlForатрибутом. Щось на зразок цього:

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

Я використовував для створення ідентифікаторів на основі, this._rootNodeIDале він недоступний з React 0.13. Який найкращий та / або найпростіший спосіб зробити це зараз?


якщо ви генеруєте цей елемент знову і знову, я припускаю, що для заяви, чому б не використовувати ітератор на ньому? Я думаю, ви також можете викликати функцію, яка генерує унікальну орієнтир, якщо число індексу недостатньо добре. stackoverflow.com/questions/105034/…
Кріс Хоукс

1
У різних компонентах є багато різних формних елементів, і всі вони повинні мати унікальні ідентифікатори. Функція генерування ідентифікаторів - це те, про що я думав, і що я буду робити, якщо ніхто не запропонує кращого рішення.
Артем Сапегін

3
Ви можете десь зберігати "глобальний" приріст лічильника і використовувати його. id = 'unique' + (++GLOBAL_ID);де var GLOBAL_ID=0;?
WiredPrairie

1
Я знаю, що я дуже і дуже спізнююсь на цю партію, але інша альтернатива - загортати вкладку в етикетку, а не використовувати ідентифікатори, наприклад:<label>My label<input type="text"/></label>
Майк Дежардінс

Відповіді:


85

Це рішення добре працює для мене.

utils/newid.js:

let lastId = 0;

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

І я можу використовувати це так:

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

Але це не працюватиме в ізоморфних додатках.

Додано 17.08.2015 . Замість спеціальної функції newId ви можете використовувати uniqueId від lodash.

Оновлено 28.01.2016 . Краще генерувати ідентифікатор у componentWillMount.


3
Тому що він почне генерувати ідентифікатори з 1-го знову в браузері. Але насправді ви можете використовувати різні префікси на сервері та в браузері.
Артем Сапегін

7
Не роби цього render! Створіть idcomponentWillMount
sarink

1
Ви створили надзвичайний контейнер, але нехтуєте використанням setState і порушуєте специфікацію render. facebook.github.io/react/docs/component-specs.html . Це має бути досить легко виправити.
аїй

3
Я використовую uniqueId від lodash у конструкторі та використовую setState для встановлення ідентифікатора. Добре працює для мого клієнта лише додаток.
CrossProduct

1
componentWillMountзастаріло, зробіть це замість цього в конструкторі. Дивіться: reactjs.org/docs/react-component.html#unsafe_componentwillmount
Вік

78

Ідентифікатор повинен бути розміщений всередині компонента компонента WillMount (оновлення на 2018 рік) constructor, а не render. Поміщення його renderзнову породжує нові ідентифікатори.

Якщо ви використовуєте підкреслення або подачу, є uniqueIdфункція, тому ваш отриманий код повинен бути приблизно таким:

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 року:

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
Або ви також можете помістити його в конструктор.
Джон Вайс

компонентWillMount застаряє з React 16.3.0, використовуйте UNSAFE_componentWillMount замість цього, див. reakctjs.org/docs/react-component.html#unsafe_componentwillmount
lokers

2
Хтось може підказати, як це робити з новими гачками в React 16.8?
Аксімілі

4
Оскільки ви не відстежуєте значення ідентифікатора, ви також можете скористатисьconst {current: id} = useRef(_uniqueId('prefix-'))
forivall

1
Яка різниця у використанні useRef замість State?
XPD

24

Після 2019-04-04, здається, це вдасться досягти за допомогою Реактивних гачків 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

Як я розумію, ви ігноруєте другий елемент масиву в деструктуризації масиву, який дозволить вам оновити id, і тепер у вас є значення, яке не буде оновлено знову протягом життя компонента.

Значенням idбуде myprefix-<n>де <n>є додаткове ціле значення, повернене з uniqueId. Якщо це для вас недостатньо унікально, подумайте про те, як зробити своє

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

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

або ознайомтеся з бібліотекою, яку я опублікував із цим тут: https://github.com/rpearce/simple-uniqueid . Є також сотні чи тисячі інших унікальних речей, що стосуються ідентифікації, але лодашів uniqueIdз префіксом має бути достатньо, щоб виконати роботу.


Оновити 2019-07-10

Завдяки @Huong Hk за те, що він вказав мені на гачки лінивого початкового стану , сума якого полягає в тому, що ви можете передати функцію, useStateяка буде запускатися лише на початковому кріпленні.

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

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

1
У мене такі ж проблеми з наданням сервера, як і багато інших способів, згаданих на цій сторінці: компонент буде повторно представлений з новим ідентифікатором у браузері.
Артем Сапегін

@ArtemSapegin: у проекті React виникла проблема ( github.com/facebook/react/isissue/1137 ), в якій обговорювалося, яким чином у компонентів є унікальні ідентифікатори, але я не думаю, що нічого з цього вийшло. Наскільки важливо, що згенеровані ідентифікатори однакові між сервером і клієнтом? Я думаю , що для того <input />, що б справа в тому , що htmlForі idатрибути повинні бути пов'язані один з одним незалежно від того , які значення.
rpearce

Важливо уникати зайвих оновлень DOM, що спричинять нові ідентифікатори.
Артем Сапегін

6
Краще, якщо ви надаєте функцію initialState№1 const [ id ] = useState(() => uniqueId('myprefix-'))замість результату функції №2 const [ id ] = useState(uniqueId('myprefix-')) . Стан: idз двох вищезазначених способів не відрізняються. Але різні uniqueId('myprefix-')будуть виконані один раз (# 1) замість кожного повторно виведеного (# 2). Дивіться: Лінивий початковий стан: reactjs.org/docs/hooks-reference.html#lazy-initial-state Як ліниво створювати дорогі об’єкти ?: reactjs.org/docs/…
Huong Nguyen

1
@HuongHk це дивовижно; Я не знав! Я
оновлю

4

Ви можете використовувати для цього бібліотеку, наприклад node-uuid, щоб переконатися, що ви отримаєте унікальні ідентифікатори.

Встановити за допомогою:

npm install node-uuid --save

Потім у свій компонент реакції додайте наступне:

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
Здається, відповідь була оновлена, щоб відповідати вимогам
Йонас Берлін

2
Це не працює в ізоморфних додатках, оскільки ідентифікатор, створений на сервері, відрізняється від генерування ідентифікатора на клієнті.
Даніель Т.

2
Але це зазначено як частину відповіді, що дуже вводить в оману
Том Маккензі

1
Я, -1 для використання УНІВЕРСАЛЬНО унікальних ідентифікаторів, це універсальний молоток для нігтів світового розміру.
Jon z

1

Сподіваємось, це корисно для всіх, хто приходить шукати універсальне / ізоморфне рішення, оскільки питання контрольної суми - це те, що призвело мене сюди насамперед.

Як було сказано вище, я створив просту утиліту для послідовного створення нового ідентифікатора. Оскільки ідентифікатори збільшуються на сервері і починаються з 0 на клієнті, я вирішив скинути приріст кожного запуску SSR.

// utility to generate ids
let current = 0

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

export function resetIdCounter () { current = 0 }

А потім у конструкторі кореневого компонента або компонентаWillMount викличте скидання. Це по суті скидає область JS для сервера в кожному рендері сервера. У клієнта це не має (і не повинно) впливати.


у вас все ще можуть виникнути сутички id, якщо клієнти знову почнуть називати введення даних із 0.
Томаш Муларчик

@Tomasz ви хочете, щоб клієнт починав повертати форму 0, щоб контрольні суми відповідали.
tenor528

0

Для звичайних звичаїв labelта input, просто простіше загортати введення в такий ярлик:

import React from 'react'

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

Крім того, це дозволяє в прапорцях / радіо кнопках застосовувати прокладки до кореневого елемента і все одно отримувати зворотній зв'язок при натисканні на вхід.


1
+1 для простоти та корисності в деяких випадках, -1 не застосовується, наприклад select, з декількома мітками на різних позиціях, нероз'єднаними компонентами інтерфейсу тощо, також рекомендується використовувати ідентифікатори a11y: Як правило, явні мітки краще підтримуються допоміжною технологією, w3. org / WAI / підручники / форми / етикетки /…
Michael B.

-1

Я знайшов таке просте рішення:

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

Я створюю унікальний модуль генератора ID (Typescript):

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

export default uniqueId;

І використовуйте верхній модуль для створення унікальних ідентифікаторів:

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

Не використовуйте ідентифікатори взагалі, якщо вам не потрібно, замість цього загортайте вкладку таким чином:

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

Тоді вам не потрібно буде турбуватися про унікальні ідентифікатори.


2
Незважаючи на те, що це підтримується HTML5, це не рекомендує доступності: "Навіть у таких випадках найкращою практикою є встановлення атрибуту, оскільки деякі допоміжні технології не розуміють неявних зв'язків між мітками та віджетами". - від developer.mozilla.org/en-US/docs/Learn/HTML/Forms/…
GuyPaddock

1
Це спосіб, який рекомендує команда React, згідно з документами, знайденими на reactjs.org/docs/forms.html
Blake Plumb

1
Команда @BlakePlumb React також має розділ доступних форм: reactjs.org/docs/accessibility.html#accessible-forms
Вік
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.