React로 필터링 가능한 목록을 구현하는 중입니다. 목록의 구조는 아래 이미지와 같습니다.
전제
작동 방식에 대한 설명은 다음과 같습니다.
- 상태는 가장 높은 수준의 구성 요소 인
Search
구성 요소에 있습니다. - 상태는 다음과 같이 설명됩니다.
{ visible : 부울, 파일 : 배열, 필터링 됨 : 배열, 쿼리 : 문자열, currentSelectedIndex : 정수 }
files
파일 경로를 포함하는 잠재적으로 매우 큰 배열입니다 (10000 개의 항목은 그럴듯한 숫자입니다).filtered
사용자가 2 자 이상을 입력 한 후 필터링 된 배열입니다. 나는 그것이 파생 데이터라는 것을 알고 있으며 그러한 주장은 그것을 상태에 저장하는 것에 대해 만들 수 있지만currentlySelectedIndex
필터링 된 목록에서 현재 선택된 요소의 색인입니다.사용자가
Input
구성 요소에 2 개 이상의 문자를 입력하면 배열이 필터링되고 필터링 된 배열의 각 항목에 대해Result
구성 요소가 렌더링됩니다.각
Result
구성 요소는 쿼리와 부분적으로 일치하는 전체 경로를 표시하고 경로의 부분 일치 부분이 강조 표시됩니다. 예를 들어, 사용자가 'le'을 입력했다면 Result 컴포넌트의 DOM은 다음과 같습니다.<li>this/is/a/fi<strong>le</strong>/path</li>
Input
구성 요소에 포커스가있는 동안 사용자가 위쪽 또는 아래쪽 키를 누르면 어레이currentlySelectedIndex
에 따라 변경filtered
됩니다. 이로 인해Result
인덱스와 일치 하는 구성 요소가 선택된 것으로 표시되어 다시 렌더링됩니다.
문제
처음에는 files
React의 개발 버전을 사용하여 충분히 작은 배열로 이것을 테스트 했으며 모두 잘 작동했습니다.
문제는 내가 files
10000 개의 항목만큼 큰 배열 을 처리해야 할 때 나타났습니다 . 입력에 2 개의 문자를 입력하면 큰 목록이 생성되고 탐색을 위해 위아래 키를 눌렀을 때 매우 느립니다.
처음에는 요소에 대해 정의 된 구성 요소가 없었으며 구성 Result
요소의 각 렌더링에서 즉시 목록을 작성했습니다 Search
.
results = this.state.filtered.map(function(file, index) {
var start, end, matchIndex, match = this.state.query;
matchIndex = file.indexOf(match);
start = file.slice(0, matchIndex);
end = file.slice(matchIndex + match.length);
return (
<li onClick={this.handleListClick}
data-path={file}
className={(index === this.state.currentlySelected) ? "valid selected" : "valid"}
key={file} >
{start}
<span className="marked">{match}</span>
{end}
</li>
);
}.bind(this));
알 수 있듯이 currentlySelectedIndex
변경 될 때마다 다시 렌더링되고 매번 목록이 다시 생성됩니다. 나는 key
각 li
요소에 값을 설정했기 때문에 React가 변경 li
되지 않은 다른 모든 요소를 다시 렌더링하는 것을 피할 것이라고 생각 className
했지만 분명히 그렇지 않았습니다.
마지막으로 Result
요소에 대한 클래스를 정의하여 각 Result
요소가 이전에 선택되었는지 여부와 현재 사용자 입력을 기반으로 다시 렌더링해야하는지 여부를 명시 적으로 확인합니다 .
var ResultItem = React.createClass({
shouldComponentUpdate : function(nextProps) {
if (nextProps.match !== this.props.match) {
return true;
} else {
return (nextProps.selected !== this.props.selected);
}
},
render : function() {
return (
<li onClick={this.props.handleListClick}
data-path={this.props.file}
className={
(this.props.selected) ? "valid selected" : "valid"
}
key={this.props.file} >
{this.props.children}
</li>
);
}
});
이제 목록이 다음과 같이 생성됩니다.
results = this.state.filtered.map(function(file, index) {
var start, end, matchIndex, match = this.state.query, selected;
matchIndex = file.indexOf(match);
start = file.slice(0, matchIndex);
end = file.slice(matchIndex + match.length);
selected = (index === this.state.currentlySelected) ? true : false
return (
<ResultItem handleClick={this.handleListClick}
data-path={file}
selected={selected}
key={file}
match={match} >
{start}
<span className="marked">{match}</span>
{end}
</ResultItem>
);
}.bind(this));
}
이로 인해 성능이 약간 향상되었지만 여전히 충분하지 않습니다. React의 프로덕션 버전에서 테스트했을 때 문제는 전혀 지연없이 부드럽게 작동했습니다.
최저 선
React의 개발 버전과 프로덕션 버전간에 눈에 띄는 불일치가 정상입니까?
React가 목록을 관리하는 방법에 대해 생각할 때 내가 이해하고 있거나 잘못하고 있습니까?
2016 년 11 월 14 일 업데이트
Michael Jackson의 프레젠테이션을 찾았습니다. 여기서 그는 다음과 매우 유사한 문제를 다루고 있습니다. https://youtu.be/7S8v8jfLb1Q?t=26m2s
솔루션은 아래 AskarovBeknar의 답변 에서 제안한 것과 매우 유사 합니다.
업데이트 2018 년 4 월 14 일
이것은 분명히 인기있는 질문이며 원래 질문이 제기 된 이후로 상황이 진전되었으므로 가상 레이아웃을 이해하려면 위에 링크 된 비디오를 시청하는 것이 좋습니다. 또한 React Virtualized 를 사용하는 것이 좋습니다. 당신이 바퀴를 재발 명하고 싶지 않다면 도서관.