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

У меня есть элементы формы с label, и я хочу иметь уникальные идентификаторы для ссылки label на элементы с атрибутом htmlFor. Что-то вроде этого:

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

Я использовал для генерации идентификаторов на основе this._rootNodeID, но его недоступно, так как React 0.13. Каков наилучший и/или самый простой способ сделать это сейчас?

Ответ 1

Эти решения отлично подходят для меня.

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.

Ответ 2

Идентификатор должен быть помещен внутри componentWillMount (обновление для 2018 года) constructor, не render. Помещение его в render будет без необходимости заново генерировать новые идентификаторы.

Если вы используете подчеркивание или lodash, есть функция 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>
  );
}

Ответ 4

По состоянию на 2019-04-04, это, кажется, можно сделать с помощью useState из React Hooks:

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. Существуют также сотни или тысячи других уникальных идентификаторов, но lodash uniqueId с префиксом должно быть достаточно для выполнения работы.


Обновление 2019-07-10

Спасибо @Huong Hk за указание на ленивое начальное состояние хуков, сумма которого заключается в том, что вы можете передать функцию useState которая будет запускаться только при начальном монтировании.

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

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

Ответ 5

Вы можете использовать такую ​​библиотеку, как 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>
    );
  }   
}

Ответ 6

Я предпочитаю просто позволить реагировать автоматически генерировать id для меня. Я не совсем уверен, что это правильное использование, поскольку подчеркивание обычно является приватным, но оно делает трюк.

this._reactInternalInstance._rootNodeID

render () {
  <button 
    id={this._reactInternalInstance._rootNodeID}>
    {this.props.children}
    {/*tooltip */}
    <div 
      htmlFor={this._reactInternalInstance._rootNodeID}
      className="mdl-tooltip mdl-tooltip--large">
      {this.props.children}
    </div>
  </button>
}

Ответ 7

Надеюсь, это поможет любому, кто ищет универсальное/изоморфное решение, поскольку проблема контрольной суммы - вот что привело меня сюда в первую очередь.

Как было сказано выше, я создал простую утилиту для последовательного создания нового идентификатора. Поскольку идентификаторы продолжают увеличиваться на сервере и начинаются с 0 в клиенте, я решил reset прирост, каждый из которых начинается с SSR.

// utility to generate ids
let current = 0

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

export function resetIdCounter () { current = 0 }

Затем в корневом компоненте или компонентеWillMount вызовите reset. Это существенно сбрасывает область JS для сервера в каждом рендеринге сервера. В клиенте это не оказывает (и не должно) никакого эффекта.

Ответ 8

Вы можете использовать id${new Date().getTime()}, поскольку new Date(). getTime() возвращает число миллисекунд с 1970/01/01 и отлично работает как уникальный идентификатор.

Следующий функциональный компонент Checkbox создает уникальный идентификатор, используемый в метке и флажке ввода для каждого рендеринга, поэтому его можно переместить на componentWillMount.

const CheckboxComponent = (props: Props) => {

    const uniqueId = `id${new Date().getTime()}`;

    return (
        <label
            htmlFor={uniqueId}
            style={[styles.container]}
        >
            <input
                id={uniqueId}
                type="checkbox"
                style={[styles.spacer]}
                {...rest}
            />
            {props.label && (
                <span style={[styles.label]}>{props.label}</span>
            )}
        </label>
    );
};
const styles = {
    label: {
        color: "#333",
    },
    container: {
        display: "flex",
        flexDirection: "row",
    },
    spacer: {
        flex: 1,
    },
};

Ответ 9

Другой простой способ с машинописью:

  static componentsCounter = 0;

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

Ответ 10

Я нашел простое решение, как это:

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

Ответ 11

Не используйте идентификаторы вообще, если вам не нужно, вместо этого оберните ввод в метку, как это:

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

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