Reactでフォームラベルの一意のIDを生成する方法は?


128

labelsを含むフォーム要素があり、s labelhtmlFor属性を含む要素にリンクするための一意のIDが必要です。このようなもの:

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

以前はに基づいてIDを生成してthis._rootNodeIDいましたが、React 0.13以降は使用できません。今それを行うための最善かつ/または最も簡単な方法は何ですか?


この要素を何度も生成している場合は、forステートメントで反復子を使用しないのはなぜでしょうか。インデックス番号が十分でない場合は、一意のGUIDを生成する関数を呼び出すこともできると思います。stackoverflow.com/questions/105034/…–
Chris Hawkes、

1
さまざまなコンポーネントにはさまざまなフォーム要素があり、それらすべてに一意のIDが必要です。IDを生成する関数は、私が考えたものであり、誰もより良い解決策を提案しない場合はどうするかです。
Artem Sapegin 2015

3
「グローバル」インクリメントカウンターをどこかに保存して、それを使用できます。 id = 'unique' + (++GLOBAL_ID);どこvar GLOBAL_ID=0;
WiredPrairie、2015

1
私はこのパーティーに非常に遅れていることを知っていますが、別の方法として、IDを使用する代わりにラベルに入力をラップすることもできます。例:<label>My label<input type="text"/></label>
Mike Desjardins

回答:


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

ただし、同形のアプリでは機能しません。

2015年8月17日を追加。代わりに、カスタムNEWIDのは、あなたが使用できる機能ユニークIDを lodashから。

2016年1月28日更新。でIDを生成することをお勧めしcomponentWillMountます。


3
ブラウザで再度1日目からIDの生成を開始するためです。しかし、実際には、サーバーとブラウザーで異なるプレフィックスを使用できます。
Artem Sapegin 2015

7
でこれを行わないでくださいrender!IDを作成componentWillMount
2016年

1
ステートフルなコンテナを作成しましたが、setStateの使用を怠っており、の仕様に違反していrenderます。facebook.github.io/react/docs/component-specs.html。ただし、修正はかなり簡単です。
2016

3
コンストラクターでlodashのuniqueIdを使用し、setStateを使用してIDを設定しています。クライアントのみのアプリでうまく機能します。
CrossProduct 2016

1
componentWillMount廃止予定です。代わりにコンストラクタでこれを実行してください。参照:reactjs.org/docs/react-component.html#unsafe_componentwillmount
Vic

78

IDは、ではなく、componentWillMount(2018の更新)内に配置する必要がconstructorありrenderます。これを入れると、render不必要に新しいIDが再生成されます。

アンダースコアまたはロダッシュを使用している場合、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
または、コンストラクタに入れることもできます。
John Weisz 2016年

componentWillMountはReact 16.3.0以降非推奨です。代わりにUNSAFE_componentWillMountを使用してください。reactjs.org
docs /

2
React 16.8の新しいフックでこれをどのように行うべきかを誰かが提案できますか?
Aximili

4
idの値を追跡していないので、次も使用できますconst {current: id} = useRef(_uniqueId('prefix-'))
forivall

1
使用状態の代わりにuseRefを使用することの違いは何ですか?
XPD

24

2019-04-04のフォローアップでは、これは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

私が理解しているように、更新を可能にする配列の構造化解除の2番目の配列項目を無視する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を確認してください。他にも数百または数千の一意のIDがありますがuniqueId、プリフィックスが付いたlodash で十分です。


アップデート2019-07-10

フック初期状態フックするように指示してくれた@Huong Hkに感謝します。その合計は、関数を渡すことができるということuseStateで、初期マウントでのみ実行されます。

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

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

1
このページに記載されている他の多くの方法と同じように、サーバーレンダリングにも同じ問題があります。ブラウザでコンポーネントが新しいIDで再レンダリングされます。
Artem Sapegin

@ArtemSapegin:Reactプロジェクトで、コンポーネントに一意のIDを設定する方法について議論している問題(github.com/facebook/react/issues/1137)がありましたが、何も起こらなかったと思います。生成されたIDがサーバーとクライアントで同じであることはどのくらい重要ですか?私はのためにと思うだろう<input />、何も問題だろうがということですhtmlForと、id属性は関係なく値が何であるかを一緒に縛られるべきではありません。
rpearce

新しいIDによって引き起こされる不要なDOM更新を回避することは重要です。
Artem Sapegin

6
関数#2 の結果ではなく、関数をinitialState#1 として提供することをお勧めします。状態:上記の2つの方法の違いはありません。しかし、異なるis は、再レンダリングされるたび(#2)ではなく、1回(#1)実行されます。参照:レイジーは初期状態:reactjs.org/docs/hooks-reference.html#lazy-initial-stateなまけ高価なオブジェクトを作成する方法?:reactjs.org/docs/...をconst [ id ] = useState(() => uniqueId('myprefix-'))const [ id ] = useState(uniqueId('myprefix-'))iduniqueId('myprefix-')
フォングエン

1
@HuongHk素晴らしいです。知らなかった!回答を更新します
rpearce

4

これにnode-uuidなどのライブラリを使用すると、一意のIDを確実に取得できます。

次を使用してインストール:

npm install node-uuid --save

次に、reactコンポーネントに以下を追加します。

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
回答は仕様に準拠するように更新されているようです
Jonas Berlin

2
サーバーで生成されたIDはクライアントで生成されたIDとは異なるため、これは同型アプリでは機能しません。
Daniel T.

2
しかし、それは非常に誤解を招く答えの一部として述べられています
トムマッケンジー

1
ええと、-1はUNIVERSALLYの一意のIDを使用することを意味します。これは、世界サイズの釘用の宇宙サイズのハンマーです。
Jon z

1

うまくいけば、チェックサムの問題が最初にここに私を導いたものなので、これは普遍的/同形のソリューションを探している人にとって役立つでしょう。

上記のように、新しいIDを順次作成するための簡単なユーティリティを作成しました。IDはサーバーで増加し続け、クライアントで0からやり直すので、SSRが開始するたびに増分をリセットすることにしました。

// utility to generate ids
let current = 0

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

export function resetIdCounter () { current = 0 }

次に、ルートコンポーネントのコンストラクターまたはcomponentWillMountで、resetを呼び出します。これは基本的に、各サーバーレンダリングでサーバーのJSスコープをリセットします。クライアントでは、効果はありません(すべきではありません)。


クライアントが再び0から入力に名前を付け始めると、IDの衝突がまだ発生する可能性があります。
Tomasz Mularczyk 2017

@Tomaszチェックサムが一致するように、クライアントにフォーム0からやり直してもらいます。
tenor528 2017

0

labelおよびの通常の使用方法では、次のinputように入力をラベルにラップする方が簡単です。

import React from 'react'

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

また、チェックボックス/ラジオボタンでルート要素にパディングを適用し、入力のクリックのフィードバックを取得することもできます。


1
+1は簡単で便利な場合があります。-1は使用できません。たとえばselect、異なる位置にある複数のラベル、結合されていないuiコンポーネントなど、IDの使用も推奨されます。 org / WAI / tutorials / forms / labels /…
マイケル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

uniqueIdジェネレーターモジュール(Typescript)を作成します。

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

export default uniqueId;

そして、トップモジュールを使用して一意のIDを生成します。

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

必要がない場合はIDをまったく使用せず、代わりに次のように入力をラベルで囲みます。

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

そうすれば、一意のIDを気にする必要はありません。


2
これはHTML5でサポートされていますが、アクセシビリティのために推奨されていません。「このような場合でも、一部の支援技術はラベルとウィジェット間の暗黙的な関係を理解し​​ないため、for属性を設定することがベストプラクティスと見なされています。」-からdeveloper.mozilla.org/en-US/docs/Learn/HTML/Forms/...
GuyPaddock

1
これは、reactjs.org
Blake Plumb

1
@BlakePlumb Reactチームには、アクセシブルなフォームセクションもあります:reactjs.org/docs/accessibility.html#accessible-forms
Vic
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.