Я вижу проблему с использованием кнопки TouchableOpacity
для управления горизонтальной прокруткой FlatList
. Если я активирую Remote Debugging
, все работает нормально, но если я отключаю отладку, тач не возвращается и пользовательский интерфейс приложения не отвечает.
Я пытаюсь реализовать горизонтальную прокрутку FlatList
, которая по сути является пошаговой wizard
пользовательским интерфейсом.
FlatList
(подкачка включена, горизонтальная) имеет от 3 до 7 элементов в массиве данных, и у меня есть кнопки prev
и next
, реализованные с компонентами TouchableOpacity
в отдельном представлении внизу экрана.
У меня есть пользовательский компонент View, который будет представлять содержимое каждого шага (на данный момент он имеет одну текстовую метку).
При нажатии на кнопку «предыдущий» или «предыдущий» следует перейти к следующему индексу в FlatList.
Используя что-то вроде:
this.flatListRef.scrollToIndex({index: this.state.currentStepIndex + 1, animated: true});
Я создал проблему в репозитории React Native GitHub. Кто-нибудь видел такое поведение? Любые известные обходные пути?
Я вижу это на Android и iOS.
Вот мое окружение:
react: ^16.3.2 => 16.3.2
react-native: ^0.55.3 => 0.55.3
Вот мой оригинальный код:
render() {
console.log('Render newrequestscreen', this.state, this.flatListRef)
return (
<SafeAreaView style={baseStyles.safeAreaDark}>
<View style={baseStyles.swipeContainerView} >
<Text style ={baseStyles.emptyListSubtitleText} > {this.state.flexItems.length == this.state.currentStepIndex ? 'Review & Submit' : 'Step ' + (this.state.currentStepIndex + 1) + ' of ' + (this.state.flexItems.length) }</Text>
</View>
<FlatList
ref={(ref) => { this.flatListRef = ref; }}
scrollEnabled={false}
initialNumToRender={1}
initialScrollIndex={0}
refreshing={false}
pagingEnabled={true}
horizontal
getItemLayout={(data, index) => (
{length: Dimensions.get('window').width, offset: Dimensions.get('window').width * index, index}
)}
data={this.state.flexItems}
showsHorizontalScrollIndicator = { false }
decelerationRate={0}
renderItem={({item, index}) => {
return (
<View style={baseStyles.swipeContainerView} pointerEvents='none' >
<Text style ={baseStyles.emptyListSubtitleText} >{item.promptText}</Text>
</View>
)
}
}
keyExtractor={(item) => item.type}/>
<View style={baseStyles.swipeContainerView} >
<View style={baseStyles.requestNavView}>
<TouchableOpacity
onPress={this._previousStep.bind(this)}
style={requestComponentStyles.locationCaptureButton}>
<Text style={requestComponentStyles.locationCaptureButtonText}>Prev Step</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this._nextStep.bind(this)}
style={requestComponentStyles.locationCaptureButton}>
<Text style={requestComponentStyles.locationCaptureButtonText}>Next Step</Text>
</TouchableOpacity>
</View>
</View>
</SafeAreaView>
);
}
_nextStep() {
console.log('next tapped...', this.flatListRef)
if (this.state.currentStepIndex < this.state.flexItems.length - 1) {
this.flatListRef.scrollToIndex({index: this.state.currentStepIndex + 1, animated: true});
this.setState(function(prevState, props) {
return {
currentStepIndex: prevState.currentStepIndex + 1
};
});
}
}
_previousStep() {
console.log('prev tapped...', this.flatListRef)
if (this.state.currentStepIndex > 0) {
this.flatListRef.scrollToIndex({index: this.state.currentStepIndex - 1, animated: true});
this.setState(function(prevState, props) {
return {
currentStepIndex: prevState.currentStepIndex - 1
};
});
}
}
Вот снимок экрана с тем, что я строю (серая область с текстом 'location' - это элемент полной ширины по горизонтали FlatList
):
Мое изображение
Я ценю любую помощь или мысли по этому поводу! Спасибо !!
Обновление / примечание: если я удаляю FlatList, кнопки TouchableOpacity будут работать нормально, поэтому определенно может возникнуть какая-то несовместимость между FlatList и другими элементами Touchable на одном экране.
Отредактировал приведенный выше код, чтобы предоставить очень простой пример этой проблемы. Если я запускаю с использованием отладчика, все хорошо, но если нет, то нажатие кнопок «Предыдущий» или «Следующий» TouchableOpacity
никогда не возвращается, и пользовательский интерфейс зависает.
Обновление: если я что-то изменю, чтобы данные моего FlatList и реквизиты currentList не были частью this.state, я могу увеличить индекс прокрутки с помощью нажатия TouchableOpacity. Проблема заключается в том, что я хотел бы обновить состояние, чтобы в режиме просмотра отображалась информация о текущем шаге (например, «Шаг x из y»).
Обновление состояния в функциях _nextStep () или _previousStep () не должно вызывать условие бесконечного цикла, верно? Я думал, что FlatList будет перерисовывать только тогда, когда изменятся реквизиты данных или extraData. Как я уже говорил ранее, если я запускаю в отладчике, я не зацикливаюсь.
Эта версия, кажется, работает - я также сделал чистую, встроенную в Xcode:
constructor(props) {
super(props)
this.state = {
stepIndex: 0
}
this.currentStepIndex = 0;
this.nextStep = this._nextStep.bind(this);
this.previousStep = this._previousStep.bind(this);
this.flexItems = [
{ promptText: 'step 1', type: 'intro' },
{ promptText: 'step 2', type: 'location' },
{ promptText: 'step 3', type: 'pickList' },
{ promptText: 'step 4', type: 'longText' },
{ promptText: 'step 5', type: 'photo' },
{ promptText: 'Review and Submit', type: 'review' }
];
this.nextStep = this._nextStep.bind(this);
this.previousStep = this._previousStep.bind(this);
console.log('State: ', this.state);
}
render() {
console.log('Render new request screen')
return (
<SafeAreaView style={baseStyles.safeAreaDark}>
<View style={baseStyles.newRequestContainerView} >
<View style={baseStyles.requestProgressView} >
<Text style ={baseStyles.emptyListSubtitleText} > {this.flexItems.length == this.state.stepIndex ? 'Review & Submit' : 'Step ' + (this.state.stepIndex + 1) + ' of ' + (this.flexItems.length) }</Text>
</View>
<FlatList
ref={(ref) => { this.flatListRef = ref; }}
scrollEnabled={false}
initialNumToRender={1}
initialScrollIndex={0}
refreshing={false}
pagingEnabled={true}
horizontal
getItemLayout={(data, index) => (
{length: Dimensions.get('window').width, offset: Dimensions.get('window').width * index, index}
)}
data={this.flexItems}
showsHorizontalScrollIndicator = { false }
decelerationRate={0}
renderItem={({item, index}) => {
if (item.type === 'intro') {
return (
<View style={baseStyles.swipeContainerView} >
<View style={baseStyles.swipeContentView} >
<Text style ={baseStyles.emptyListSubtitleText}>All About Potholes</Text>
</View>
</View>
)
} else if (item.type === 'pickList') {
return (
<View style={baseStyles.swipeContainerView} >
<View style={baseStyles.swipeContentView} >
<Text style ={baseStyles.emptyListSubtitleText} >{item.promptText}</Text>
</View>
</View>
)
} else {
return (
<View style={baseStyles.swipeContainerView} >
<View style={baseStyles.swipeContentView} >
<Text style ={baseStyles.emptyListSubtitleText} >{item.promptText}</Text>
</View>
</View>
)
}
}}
keyExtractor={(item) => item.type}/>
</View>
<View style={baseStyles.requestNavView}>
<TouchableOpacity
onPress={this.previousStep}
style={requestComponentStyles.locationCaptureButton}>
<Text style={requestComponentStyles.locationCaptureButtonText}>Prev Step</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.nextStep}
style={requestComponentStyles.locationCaptureButton}>
<Text style={requestComponentStyles.locationCaptureButtonText}> {this.state.stepIndex < this.flexItems.length - 1 ? 'Next Step' : 'Submit'}</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
_nextStep() {
console.log('next tapped...')
if (this.currentStepIndex < this.flexItems.length - 1) {
this.currentStepIndex = this.currentStepIndex + 1;
this.flatListRef.scrollToIndex({index: this.currentStepIndex, animated: true});
this.setState (
{
stepIndex: this.currentStepIndex
}
)
} else {
this._onDismissScreen();
}
}
_previousStep () {
console.log('prev tapped...')
if (this.currentStepIndex > 0) {
this.currentStepIndex = this.currentStepIndex - 1;
this.flatListRef.scrollToIndex({index: this.currentStepIndex, animated: true});
this.setState (
{
stepIndex: this.currentStepIndex
}
)
}
}