배열을 반복 할 때주의하십시오 !!
배열에서 요소의 인덱스를 사용하는 것이 익숙한 오류를 억제하는 데 허용되는 방법이라는 것은 일반적인 오해입니다.
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에서 배열을 반복 할 때 가장 흔한 실수입니다. 이 접근법은 기술적으로 "잘못" 되지 않습니다. 당신이 무엇을하고 있는지 모른다면 그것은 단지 "위험한" 것입니다. 정적 배열을 반복하는 경우 이는 완벽하게 유효한 접근 방법입니다 (예 : 탐색 메뉴의 링크 배열). 그러나 항목을 추가, 제거, 재정렬 또는 필터링하는 경우주의해야합니다. 공식 문서 에서이 자세한 설명 을 살펴보십시오 .
class MyApp extends React.Component {
constructor() {
super();
this.state = {
arr: ["Item 1"]
}
}
click = () => {
this.setState({
arr: ['Item ' + (this.state.arr.length+1)].concat(this.state.arr),
});
}
render() {
return(
<div>
<button onClick={this.click}>Add</button>
<ul>
{this.state.arr.map(
(item, i) => <Item key={i} text={"Item " + i}>{item + " "}</Item>
)}
</ul>
</div>
);
}
}
const Item = (props) => {
return (
<li>
<label>{props.children}</label>
<input value={props.text} />
</li>
);
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
이 스 니펫에서는 정적이 아닌 배열을 사용하고 있으며이를 스택으로 사용하도록 제한하지 않습니다. 이것은 안전하지 않은 접근 방식입니다 (이유를 알 수 있습니다). 배열의 시작 부분에 항목을 추가 할 때 (기본적으로 이동하지 않음) 각 값이 <input>
그대로 유지됩니다. 왜? 때문에 key
고유의 각 항목을 식별하지 않습니다.
다시 말해, 처음 Item 1
에는 key={0}
입니다. 두 번째 항목을 추가하면 맨 위 항목이 Item 2
이고 그 다음으로 Item 1
두 번째 항목이됩니다. 그러나 지금 Item 1
이 key={1}
아니라 key={0}
더 이상. 대신, Item 2
이제는 key={0}
!!
따라서 React <input>
는 Item
with 키 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 .
key
속성 이 있어야 합니다. ReactJS가 적절한 DOM 노드에 대한 참조를 찾고 마크 업 내의 내용 만 업데이트하지만 전체 테이블 / 행을 다시 렌더링하지 않도록 도와줍니다.