[javascript] React.js의 배열 자식에 대한 고유 키 이해

JSON 데이터 원본을 허용하고 정렬 가능한 테이블을 만드는 React 구성 요소를 작성 중입니다.
각 동적 데이터 행에는 고유 키가 할당되어 있지만 여전히 다음과 같은 오류가 발생합니다.

배열의 각 자식에는 고유 한 “키”소품이 있어야합니다.
TableComponent의 렌더링 방법을 확인하십시오.

TableComponent렌더 메소드는 다음을 반환합니다.

<table>
  <thead key="thead">
    <TableHeader columns={columnNames}/>
  </thead>
  <tbody key="tbody">
    { rows }
  </tbody>
</table>

TableHeader구성 요소는 단일 행이며, 또한 할당 된 고유 키가 있습니다.

각 입력 rowrows고유 키가있는 구성 요소로 빌드됩니다.

<TableRowItem key={item.id} data={item} columns={columnNames}/>

그리고 TableRowItem다음과 같이 보입니다 :

var TableRowItem = React.createClass({
  render: function() {

    var td = function() {
        return this.props.columns.map(function(c) {
          return <td key={this.props.data[c]}>{this.props.data[c]}</td>;
        }, this);
      }.bind(this);

    return (
      <tr>{ td(this.props.item) }</tr>
    )
  }
});

고유 키 소품 오류의 원인은 무엇입니까?



답변

각 하위 요소와 하위 내부의 각 요소에 키를 추가해야합니다 .

이런 식으로 React는 최소 DOM 변경을 처리 할 수 ​​있습니다.

귀하의 코드에서 각각 <TableRowItem key={item.id} data={item} columns={columnNames}/>은 키가없는 일부 어린이를 렌더링하려고합니다.

이 예를 확인하십시오 .

div 내부 key={i}<b></b>요소 에서를 제거하고 콘솔을 확인하십시오.

샘플에서, 우리는 열쇠 제공하지 않는 경우 <b>요소를 우리 업데이트 할 경우에만object.city에 요구 반작용 단지 요소 대 전체 행을 재 렌더링.

코드는 다음과 같습니다.

var data = [{name:'Jhon', age:28, city:'HO'},
            {name:'Onhj', age:82, city:'HN'},
            {name:'Nohj', age:41, city:'IT'}
           ];

var Hello = React.createClass({

    render: function() {

      var _data = this.props.info;
      console.log(_data);
      return(
        <div>
            {_data.map(function(object, i){
               return <div className={"row"} key={i}>
                          {[ object.name ,
                             // remove the key
                             <b className="fosfo" key={i}> {object.city} </b> ,
                             object.age
                          ]}
                      </div>;
             })}
        </div>
       );
    }
});

React.render(<Hello info={data} />, document.body);

@Chris 가 맨 아래에 게시 한 답변은 이 답변보다 훨씬 자세합니다. https://stackoverflow.com/a/43892905/2325522참조하십시오

조정시 의 중요성에 대한 문서화 :


답변

배열을 반복 할 때주의하십시오 !!

배열에서 요소의 인덱스를 사용하는 것이 익숙한 오류를 억제하는 데 허용되는 방법이라는 것은 일반적인 오해입니다.

Each child in an array should have a unique "key" prop.

그러나 많은 경우에 그렇지 않습니다! 이것은 일부 상황에서 원치 않는 동작을 유발할 수있는 안티 패턴 입니다 .

key소품 이해

React는 key소품을 사용하여 구성 요소 -DOM 요소 관계를 이해하고 조정 프로세스에 사용 됩니다 . 따라서 키가 항상 고유 한 상태를 유지 하는 것이 매우 중요합니다. 그렇지 않으면 React가 요소를 혼합하고 잘못된 요소를 변형시킬 가능성이 높습니다. 최상의 성능을 유지하려면 이러한 키 를 모든 다시 렌더링에서 정적으로 유지 하는 것이 중요합니다 .

즉 , 배열이 완전히 정적이라는 것이 알려진 경우 위의 사항을 항상 적용 할 필요 는 없습니다 . 그러나 가능하면 모범 사례를 적용하는 것이 좋습니다.

React 개발자는 이 GitHub 문제 에서 다음같이 말했습니다 .

  • 핵심은 실제로 성능에 관한 것이 아니라 정체성에 관한 것입니다 (성능 향상으로 이어집니다). 임의로 할당 및 변경되는 값은 동일하지 않습니다
  • 데이터 모델링 방식을 알지 못하면 현실적으로 키를 [자동으로] 제공 할 수 없습니다. ID가 없으면 일종의 해싱 함수를 사용하는 것이 좋습니다.
  • 배열을 사용할 때 이미 내부 키가 있지만 배열의 인덱스입니다. 새 요소를 삽입하면 해당 키가 잘못되었습니다.

즉, a는 key해야한다 :

  • 고유 -키는 형제 구성 요소 의 키와 동일 할 수 없습니다 .
  • 정적 -렌더링간에 키가 변경되지 않아야합니다.

key소품 사용

위의 설명에 따라 다음 샘플을주의 깊게 연구하고 가능한 경우 권장되는 접근법을 구현하십시오.


불량 (잠재적으로)

<tbody>
    {rows.map((row, i) => {
        return <ObjectRow key={i} />;
    })}
</tbody>

이것은 아마도 React에서 배열을 반복 할 때 가장 흔한 실수입니다. 이 접근법은 기술적으로 “잘못” 되지 않습니다. 당신이 무엇을하고 있는지 모른다면 그것은 단지 “위험한” 것입니다. 정적 배열을 반복하는 경우 이는 완벽하게 유효한 접근 방법입니다 (예 : 탐색 메뉴의 링크 배열). 그러나 항목을 추가, 제거, 재정렬 또는 필터링하는 경우주의해야합니다. 공식 문서 에서이 자세한 설명 을 살펴보십시오 .

이 스 니펫에서는 정적이 아닌 배열을 사용하고 있으며이를 스택으로 사용하도록 제한하지 않습니다. 이것은 안전하지 않은 접근 방식입니다 (이유를 알 수 있습니다). 배열의 시작 부분에 항목을 추가 할 때 (기본적으로 이동하지 않음) 각 값이 <input>그대로 유지됩니다. 왜? 때문에 key고유의 각 항목을 식별하지 않습니다.

다시 말해, 처음 Item 1에는 key={0}입니다. 두 번째 항목을 추가하면 맨 위 항목이 Item 2이고 그 다음으로 Item 1두 번째 항목이됩니다. 그러나 지금 Item 1key={1}아니라 key={0}더 이상. 대신, Item 2이제는 key={0}!!

따라서 React <input>Itemwith 키 0가 항상 맨 위에 있기 때문에 요소가 변경되지 않았다고 생각합니다 !

그렇다면 왜이 접근법은 때때로 나쁜 것일까 요?

이 방법은 어레이가 어떻게 든 필터링, 재 배열되거나 항목이 추가 / 제거되는 경우에만 위험합니다. 항상 정적 인 경우 사용하는 것이 안전합니다. 예를 들어, ["Home", "Products", "Contact us"]새로운 링크를 추가하거나 재 배열하지 않기 때문에이 방법 으로 탐색 메뉴 를 안전하게 반복 할 수 있습니다.

간단히 말해서 다음과 같이 인덱스를 안전하게 사용할 수 있습니다 key.

  • 배열은 정적이며 변경되지 않습니다.
  • 배열은 필터링되지 않습니다 (어레이의 하위 집합 표시).
  • 배열은 다시 정렬되지 않습니다.
  • 배열은 스택 또는 LIFO (last in, first out)로 사용됩니다. 다시 말해, 추가는 배열의 끝에서만 (즉 푸시) 수행 할 수 있으며 마지막 항목 만 제거 (즉 팝) 할 수 있습니다.

대신 위의 스 니펫 에서 추가 된 항목을 배열의 끝으로 밀면 기존의 각 항목의 순서는 항상 정확합니다.


아주 나쁜

<tbody>
    {rows.map((row) => {
        return <ObjectRow key={Math.random()} />;
    })}
</tbody>

이 방법은 아마도 키의 고유성을 보장 할 것이지만, 이것이 필요하지 않더라도 항상 목록의 각 항목을 다시 렌더링하도록 강제적으로 반응합니다. 성능에 큰 영향을 미치기 때문에 이것은 매우 나쁜 해결책입니다. Math.random()동일한 숫자를 두 번 생성하는 경우 키 충돌 가능성을 배제 할 수는 없습니다 .

불안정한 키 (에서 생성 한 것과 같은 키 Math.random())는 많은 구성 요소 인스턴스와 DOM 노드가 불필요하게 다시 만들어져 하위 구성 요소의 성능 저하 및 상태 손실을 유발할 수 있습니다.


아주 좋아

<tbody>
    {rows.map((row) => {
        return <ObjectRow key={row.uniqueId} />;
    })}
</tbody>

이것은 틀림없이입니다 가장 좋은 방법 은 데이터 세트의 각 항목에 대해 고유 한 속성을 사용하기 때문이다. 예를 들어, rows데이터베이스에서 가져온 데이터 가 포함 된 경우 테이블의 기본 키 ( 일반적으로 자동 증분 번호 )를 사용할 있습니다.

키를 선택하는 가장 좋은 방법은 형제 중에서 목록 항목을 고유하게 식별하는 문자열을 사용하는 것입니다. 대부분의 경우 데이터의 ID를 키로 사용합니다


좋은

componentWillMount() {
  let rows = this.props.rows.map(item => {
    return {uid: SomeLibrary.generateUniqueID(), value: item};
  });
}

...

<tbody>
    {rows.map((row) => {
        return <ObjectRow key={row.uid} />;
    })}
</tbody>

이것은 또한 좋은 접근 방법입니다. 데이터 세트에 고유성을 보장하는 데이터 ( 예 : 임의의 숫자 배열)가 포함되어 있지 않으면 키 충돌이 발생할 수 있습니다. 이러한 경우 반복하기 전에 데이터 집합의 각 항목에 대해 고유 식별자를 수동으로 생성하는 것이 가장 좋습니다. 구성 요소를 다시 렌더링 할 때마다가 아니라 한 번만 수행하기 위해 구성 요소를 마운트 할 때 또는 데이터 세트가 수신 될 때 ( 예 : props비동기 API 호출 에서 또는 비동기 API 호출에서 ) 바람직 합니다. 그러한 키를 제공 할 수있는 소수의 라이브러리가 이미 있습니다. 예를 들면 다음과 같습니다. react-key-index .


답변

이것은 누군가에게 도움이 될 수도 있고 도움이되지 않을 수도 있지만 빠른 참조 일 수도 있습니다. 이것은 또한 위에 제시된 모든 답변과 유사합니다.

아래 구조를 사용하여 목록을 생성하는 많은 위치가 있습니다.

return (
    {myList.map(item => (
       <>
          <div class="some class">
             {item.someProperty}
              ....
          </div>
       </>
     )}
 )

약간의 시행 착오 (및 일부 좌절) 후 가장 바깥 쪽 블록에 키 속성을 추가하면 문제가 해결되었습니다. 또한 <> 태그는 이제 태그로 대체되었습니다.

return (

    {myList.map((item, index) => (
       <div key={index}>
          <div class="some class">
             {item.someProperty}
              ....
          </div>
       </div>
     )}
 )

물론 위의 예제에서 키를 반복하기 위해 순회 반복 인덱스 (인덱스)를 사용했습니다. 이상적으로는 목록 항목에 고유 한 것을 사용하는 것이 좋습니다.


답변

경고 : 배열 또는 반복자의 각 자식에는 고유 한 “키”소품이 있어야합니다.

반복하려는 배열 항목의 경우 고유 한 유사성이 필요하다는 경고입니다.

React는 반복적 인 컴포넌트 렌더링을 배열로 처리합니다.

이를 해결하는 더 좋은 방법은 반복하려는 배열 항목에 대한 색인을 제공하는 것입니다.

class UsersState extends Component
    {
        state = {
            users: [
                {name:"shashank", age:20},
                {name:"vardan", age:30},
                {name:"somya", age:40}
            ]
        }
    render()
        {
            return(
                    <div>
                        {
                            this.state.users.map((user, index)=>{
                                return <UserState key={index} age={user.age}>{user.name}</UserState>
                            })
                        }
                    </div>
                )
        }

index는 React 내장 소품입니다.


답변

구성 요소에 고유 키 를 추가하기 만하면됩니다

data.map((marker)=>{
    return(
        <YourComponents
            key={data.id}     // <----- unique key
        />
    );
})


답변

확인 : 키 = undef !!!

또한 경고 메시지가 나타납니다.

Each child in a list should have a unique "key" prop.

코드가 올바르게 완료되었지만 켜져 있으면

<ObjectRow key={someValue} />

someValue가 정의되지 않았습니다 !!! 먼저 확인하십시오. 시간을 절약 할 수 있습니다.


답변

반응하는 고유 키 정의의 가장 좋은 해결책 : 맵 내에서 이름 게시물을 초기화 한 다음 key = {post.id}로 키 정의를 정의하거나 내 코드에서 이름 항목을 정의한 다음 key = {item.id로 키를 정의하는 것을 볼 수 있습니다 } :

<div className="container">
                {posts.map(item =>(

                    <div className="card border-primary mb-3" key={item.id}>
                        <div className="card-header">{item.name}</div>
                    <div className="card-body" >
                <h4 className="card-title">{item.username}</h4>
                <p className="card-text">{item.email}</p>
                    </div>
                  </div>
                ))}
            </div>