React Native - хорошая практика: SegmentedControlIOS с ListView
Каковы лучшие практики в реализации SegmentedControllIOS
с ListView
? Я пробовал три решения, все примеры содержат SegmentedControllIOS
с двумя сегментами и двумя ListView
, Я приглашаю вас обсудить эффективность этих трех (возможно, кто-то мог бы предложить другое, лучшее решение). С моей точки зрения примеры приведены в порядке наиболее эффективных.
1. Два независимых источника данных, один ListView (изменить источник данных ListView)
class Example extends Component {
constructor(props) {
super(props);
this.state = {
ds1: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2,}),
ds2: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2,}),
index: 0,
};
}
render() {
return (
<View>
<SegmentedControlIOS
selectedIndex={this.state.index}
values={['ds1', 'ds2']}
onChange={() => this.setState({index: (this.state.index+1)%2})}
/>
<ListView dataSource={this.state.index ? this.state.ds2 : this.state.ds1} />
</View>
);
}
}
2. Два независимых источника данных и два независимых ListView
class Example extends Component {
constructor(props) {
super(props);
this.state = {
ds1: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2,}),
ds2: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2,}),
index: 0,
};
}
render() {
return (
<View>
<SegmentedControlIOS
selectedIndex={this.state.index}
values={['ds1', 'ds2']}
onChange={() => this.setState({index: (this.state.index+1)%2})}
/>
{this.state.index === 0 ?
(<ListView dataSource={this.state.ds1} />)
:
(<ListView dataSource={this.state.ds2} />)
}
</View>
);
}
}
3. Один источник данных, cloneWithRows на источнике данных при изменении индекса
class Example extends Component {
constructor(props) {
super(props);
this.state = {
ds: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2,}),
ds1: ['some', 'data'],
ds2: ['some', 'other', 'data'],
index: 0,
};
this.onChange = this.onChange.bind(this);
}
onChange() {
this.setState({
ds: this.state.ds.cloneWithRows(this.state.index ? this.ds1 : this.ds2),
index: (this.state.index+1)%2,
})
}
render() {
return (
<View>
<SegmentedControlIOS
selectedIndex={this.state.index}
values={['ds1', 'ds2']}
onChange={this.onChange}
/>
<ListView dataSource={this.state.ds} />
</View>
);
}
}
2 ответа
Третий путь будет лучшим. Когда вы используете cloneWithRows
ListView
использует DataSource
"s rowHasChanged
Функция, чтобы узнать, какие строки теперь нужно перерисовать. поскольку 'some'
в первом ряду ds1
будет соответствовать 'some'
в первом ряду ds2
этот ряд не будет перерисован.
В случае 1 вы не пользуетесь DataSource
состояние объекта, ListView
видит, что пытается отобразить совершенно другой (возможно, несопоставимый) источник данных.
В случае 2 вы можете получить некоторые забавные артефакты рендеринга, отключив тяжелый прокручиваемый компонент.
Я думаю, это зависит от варианта использования
IMO, я выберу opt2 для начала, потому что это просто и легко отлаживать, и opt1, если есть какие-либо проблемы с производительностью (также проверьте, как реализован FlatList: https://reactnative.dev/docs/optimizing-flatlist-configuration). Я чувствую, что opt1 и opt3 могут привести к тому, что list1 отображает данные list2 и наоборот.
Я думаю, что нам редко нужно рендерить большое количество строк с первого раза. Обычно мы используем разбиение на страницы (или непрерывную загрузку при прокрутке вниз), и с этой загрузкой, когда вы переключаете сегмент, данные обновляются, и вам не нужно отображать большие строки.
Плюсы opt2:
- легко понять код
- обрабатывать renderRow и selectRow также проще, так как у вас есть 2 отдельных списка.
Я могу перейти к opt1 при возникновении каких-либо проблем, потому что на opt2 весь список повторно монтируется при переключении вкладки, что не очень хорошо (на самом деле, мы можем просто скрыть список, а не отключать его, это потребует больше памяти, но более быстрый рендер )
И производительность по-прежнему плохая, я рассмотрю возможность реализации на нативной стороне:D