จะสร้าง ID เฉพาะสำหรับป้ายกำกับแบบฟอร์มใน React ได้อย่างไร?


129

ฉันมีองค์ประกอบของรูปแบบที่มีlabels และฉันต้องการมีรหัสเฉพาะเพื่อเชื่อมโยงlabels กับองค์ประกอบที่มีhtmlForแอตทริบิวต์ สิ่งนี้:

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

ฉันเคยสร้าง ID ตามthis._rootNodeIDแต่ใช้ไม่ได้ตั้งแต่ React 0.13 อะไรคือวิธีที่ดีที่สุดและ / หรือง่ายที่สุดในตอนนี้?


หากคุณสร้างองค์ประกอบนี้ซ้ำแล้วซ้ำอีกฉันคิดว่าในคำสั่ง for ทำไมไม่ใช้ iterator กับมัน? ฉันคิดว่าคุณสามารถเรียกใช้ฟังก์ชันที่สร้าง guid เฉพาะได้หากหมายเลขดัชนีไม่ดีพอ stackoverflow.com/questions/105034/…
Chris Hawkes

1
มีองค์ประกอบรูปแบบที่แตกต่างกันมากมายในส่วนประกอบต่างๆและองค์ประกอบทั้งหมดควรมีรหัสที่ไม่ซ้ำกัน ฟังก์ชันในการสร้าง ID คือสิ่งที่ฉันคิดและฉันจะทำอย่างไรหากไม่มีใครแนะนำวิธีแก้ปัญหาที่ดีกว่า
Artem Sapegin

3
คุณสามารถจัดเก็บตัวนับส่วนเพิ่ม "ส่วนกลาง" ไว้ที่ไหนสักแห่งและใช้สิ่งนั้น id = 'unique' + (++GLOBAL_ID);ที่ไหนvar GLOBAL_ID=0;?
WiredPrairie

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

แต่จะใช้ไม่ได้ในแอป isomorphic

เพิ่ม 2015/08/17 แทนที่จะใช้ฟังก์ชัน newId ที่กำหนดเองคุณสามารถใช้uniqueIdจาก lodash ได้

Updated 2016/01/28 componentWillMountมันจะดีกว่าที่จะสร้างรหัสใน


3
เพราะมันจะเริ่มสร้าง ID จากที่ 1 อีกครั้งในเบราว์เซอร์ แต่จริงๆแล้วคุณสามารถใช้คำนำหน้าต่างกันบนเซิร์ฟเวอร์และในเบราว์เซอร์
Artem Sapegin

7
อย่าทำแบบนี้render! สร้างรหัสในcomponentWillMount
sarink

1
คุณได้ทำภาชนะ stateful แต่จะมีการละเลยที่จะใช้ setState renderและมีการละเมิดข้อมูลจำเพาะสำหรับ facebook.github.io/react/docs/component-specs.html มันควรจะค่อนข้างง่ายในการแก้ไข
aij

3
ฉันใช้ uniqueId จาก lodash ในตัวสร้างและใช้ setState เพื่อตั้งค่า id ทำงานได้ดีสำหรับแอปไคลเอ็นต์ของฉันเท่านั้น
CrossProduct

1
componentWillMountเลิกใช้แล้วให้ทำสิ่งนี้ในตัวสร้างแทน ดูreactjs.org/docs/react-component.html#unsafe_componentwillmount
วิก

79

รหัสควรจะวางไว้ด้านในของ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>
  );
}

อัปเดต Hooks 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

componentWillMount เลิกใช้แล้วตั้งแต่ React 16.3.0 ใช้ UNSAFE_componentWillMount แทนโปรดดูreactjs.org/docs/react-component.html#unsafe_componentwillmount
lokers

2
ใครช่วยแนะนำวิธีนี้กับ Hooks ใหม่ใน React 16.8 ได้บ้าง?
Aximili

4
เนื่องจากคุณไม่ได้ติดตามค่าของรหัสคุณสามารถใช้const {current: id} = useRef(_uniqueId('prefix-'))
forivall

1
อะไรคือความแตกต่างกับการใช้ useRef แทน use State?
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

ตามที่ฉันเข้าใจคุณไม่สนใจรายการอาร์เรย์ที่สองในการทำลายโครงสร้างอาร์เรย์ที่อนุญาตให้คุณอัปเดต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 ที่เป็นเอกลักษณ์อื่น ๆ อีกหลายร้อยหรือหลายพันรายการ แต่ lodash ที่uniqueIdมีคำนำหน้าก็น่าจะเพียงพอที่จะทำงานให้ลุล่วง


อัปเดต 2019-07-10

ขอบคุณ @Huong Hk ที่ชี้ให้ฉันเห็นสถานะเริ่มต้นที่ขี้เกียจผลรวมคือคุณสามารถส่งผ่านฟังก์ชั่นไปuseStateที่จะทำงานบนการเมานต์เริ่มต้นเท่านั้น

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

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

1
ฉันมีปัญหาเดียวกันกับการแสดงผลเซิร์ฟเวอร์เช่นเดียวกับวิธีการอื่น ๆ ที่กล่าวถึงในหน้านี้: คอมโพเนนต์จะแสดงผลด้วย ID ใหม่ในเบราว์เซอร์
Artem Sapegin

@ArtemSapegin: มีปัญหา ( github.com/facebook/react/issues/1137 ) ในโครงการ React ที่พูดคุยเกี่ยวกับการมีส่วนประกอบที่มีรหัสเฉพาะ แต่ฉันไม่คิดว่ามีอะไรเกิดขึ้น ID ที่สร้างขึ้นมีความสำคัญเพียงใดระหว่างเซิร์ฟเวอร์และไคลเอนต์ ฉันคิดว่าสำหรับ an <input />สิ่งที่สำคัญคือควรผูกแอตทริบิวต์htmlForและidเข้าด้วยกันไม่ว่าค่าจะเป็นเท่าใดก็ตาม
rpearce

สิ่งสำคัญคือต้องหลีกเลี่ยงการอัปเดต DOM ที่ไม่จำเป็นซึ่งจะทำให้เกิด ID ใหม่
Artem Sapegin

6
จะดีกว่าถ้าคุณให้ฟังก์ชันเป็นinitialState# 1 const [ id ] = useState(() => uniqueId('myprefix-'))แทนที่จะเป็นผลลัพธ์ของฟังก์ชัน # 2 const [ id ] = useState(uniqueId('myprefix-')) สถานะ: idจาก 2 วิธีข้างต้นไม่แตกต่างกัน แต่สิ่งที่แตกต่างuniqueId('myprefix-')จะถูกดำเนินการหนึ่งครั้ง (# 1) แทนที่จะแสดงผลใหม่ทุกครั้ง (# 2) ดู: สถานะเริ่มต้นขี้เกียจ: reactjs.org/docs/hooks-reference.html#lazy-initial-stateวิธีสร้างวัตถุราคาแพงอย่างเกียจคร้าน: reactjs.org/docs/…
Huong Nguyen

1
@HuongHk ที่น่าทึ่ง; ฉันไม่รู้! ฉันจะอัปเดตคำตอบ
rpearce

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

3
สารไม่บริสุทธิ์renderละเมิดfacebook.github.io/react/docs/component-specs.html
aij

2
คำตอบดูเหมือนจะได้รับการอัปเดตเพื่อให้สอดคล้องกับข้อมูลจำเพาะ
Jonas Berlin

2
สิ่งนี้ใช้ไม่ได้ในแอป isomorphic เนื่องจาก id ที่สร้างบนเซิร์ฟเวอร์แตกต่างจาก id ที่สร้างบนไคลเอนต์
Daniel T.

2
แต่มีการระบุว่าเป็นส่วนหนึ่งของคำตอบซึ่งทำให้เข้าใจผิดมาก
Tom McKenzie

1
ใช่ -1 สำหรับการใช้รหัสเฉพาะ UNIVERSALLY นั่นคือค้อนขนาดจักรวาลสำหรับตะปูขนาดโลก
Jon z

1

หวังว่านี่จะเป็นประโยชน์สำหรับทุกคนที่กำลังมองหาโซลูชันสากล / isomorphic เนื่องจากปัญหาการตรวจสอบคือสิ่งที่ทำให้ฉันมาที่นี่ตั้งแต่แรก

ดังที่ได้กล่าวไว้ข้างต้นฉันได้สร้างยูทิลิตี้ง่ายๆเพื่อสร้างรหัสใหม่ตามลำดับ เนื่องจาก 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 ให้เรียกการรีเซ็ต สิ่งนี้จะรีเซ็ตขอบเขต JS สำหรับเซิร์ฟเวอร์ในการแสดงผลเซิร์ฟเวอร์แต่ละครั้ง ในไคลเอนต์มันไม่มี (และไม่ควร) มีผลใด ๆ


คุณยังคงมี id clashes หากไคลเอ็นต์เริ่มตั้งชื่ออินพุตจาก 0 อีกครั้ง
Tomasz Mularczyk

@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ป้ายกำกับหลายตำแหน่งในตำแหน่งที่ต่างกันส่วนประกอบ ui ที่ไม่ได้แยกออกเป็นต้นแนะนำให้ใช้รหัส 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

ฉันสร้างโมดูลตัวสร้าง uniqueId (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

อย่าใช้ ID เลยหากคุณไม่ต้องการให้ห่ออินพุตไว้ในป้ายกำกับเช่นนี้แทน:

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

จากนั้นคุณจะไม่ต้องกังวลเกี่ยวกับ ID ที่ไม่ซ้ำกัน


2
แม้ว่า HTML5 จะรองรับสิ่งนี้ แต่ก็ไม่แนะนำสำหรับความสามารถในการเข้าถึง: "แม้ในกรณีดังกล่าวจะถือว่าเป็นแนวทางปฏิบัติที่ดีที่สุดในการตั้งค่าแอตทริบิวต์สำหรับเนื่องจากเทคโนโลยีช่วยเหลือบางอย่างไม่เข้าใจความสัมพันธ์โดยนัยระหว่างป้ายกำกับและวิดเจ็ต" - จากdeveloper.mozilla.org/en-US/docs/Learn/HTML/Forms/…
GuyPaddock

1
นี่เป็นวิธีที่ทีม React แนะนำตามเอกสารที่reactjs.org/docs/forms.html
Blake Plumb

1
ทีมปฏิกิริยา @BlakePlumb ยังมีส่วนแบบฟอร์มที่สามารถเข้าถึงได้: reactjs.org/docs/accessibility.html#accessible-forms
Vic
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.