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인덱스와 일치 하는 구성 요소가 선택된 것으로 표시되어 다시 렌더링됩니다.
문제
처음에는 filesReact의 개발 버전을 사용하여 충분히 작은 배열로 이것을 테스트 했으며 모두 잘 작동했습니다.
문제는 내가 files10000 개의 항목만큼 큰 배열 을 처리해야 할 때 나타났습니다 . 입력에 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 를 사용하는 것이 좋습니다. 당신이 바퀴를 재발 명하고 싶지 않다면 도서관.



