React Native - как прокрутить ScrollView в заданное место после перехода с другого экрана

Можно ли сказать ScrollView для прокрутки до определенной позиции, когда мы только что перешли на текущий экран через StackNavigator?

У меня два экрана; Меню и предметы. Меню представляет собой список кнопок, по одной для каждого элемента. Экран Предметов содержит Карусель, построенную с использованием ScrollView с изображением и подробным описанием каждого Предмета.

Когда я нажимаю кнопку на экране меню, я хочу перейти к экрану "Элементы" и автоматически перейти к элементу, который представляет кнопка.

Я прочитал, что вы можете передавать параметры при использовании StackNavigator следующим образом: но я не знаю, как считать этот параметр на моем экране Items.

navigate('Items', { id: '1' })

Так возможно ли это в React Native и как мне это сделать? Или, возможно, я использую не тот навигатор?

Вот глупая версия моих двух экранов:

App.js:

const SimpleApp = StackNavigator({
    Menu: { screen: MenuScreen},
    Items: { screen: ItemScreen }
  }
);

export default class App extends React.Component {
  render() {
    return <SimpleApp />;
  }
}

Menu.js

export default class Menu extends React.Component {
    constructor(props){
        super(props)
        this.seeDetail = this.seeDetail.bind(this)
    }

    seeDetail(){
        const { navigate } = this.props.navigation;
        navigate('Items')
    }

    render(){
        <Button onPress={this.seeDetail} title='1'/>
        <Button onPress={this.seeDetail} title='2'/>
    }
}

Items.js

export default class Items extends React.Component {
  render(){
    let scrollItems = [] //Somecode that generates and array of items
    return (
      <View>
        <View style={styles.scrollViewContainer}>
          <ScrollView 
          horizontal
          pagingEnabled
          ref={(ref) => this.myScroll = ref}>
            {scrollItems}
          </ScrollView>
        </View>
      </View>
    )
  }  
}

PS В данный момент я специально ориентируюсь на Android, но в идеале может быть кроссплатформенное решение.

2 ответа

Решение

Я прочитал, что вы можете передавать параметры при использовании StackNavigator следующим образом: но я не знаю, как считать этот параметр на моем экране Items.

Это достигается путем доступа this.props.navigation.state.params внутри вашего дочернего компонента.

Я думаю, что лучшее время позвонить scrollTo на вашей прокрутки ссылка, когда он впервые назначен. Вы уже даете ему ссылку и запускаете функцию обратного вызова - я бы просто настроил ее так, чтобы она также вызывала scrollTo в то же время:

export default class Items extends React.Component {
  render(){
    let scrollItems = [] //Somecode that generates and array of items
    const {id} = this.props.navigation.state.params;

    return (
      <View>
        <View style={styles.scrollViewContainer}>
          <ScrollView 
          horizontal
          pagingEnabled
          ref={(ref) => {
            this.myScroll = ref
            this.myScroll.scrollTo() // !!
          }>
            {scrollItems}
          </ScrollView>
        </View>
      </View>
    )
  }  
}

И именно поэтому я использую FlatLists или SectionLists (которые наследуются от VirtualizedList) вместо ScrollViews. VirtualizedList имеет функцию scrollToIndex, которая гораздо более интуитивно понятна. ScrollTo ScrollView ожидает параметры x и y, означающие, что вам нужно будет рассчитать точное место для прокрутки - умножить ширину каждого элемента прокрутки на индекс элемента, на который вы прокручиваете. И если для каждого элемента задействовано заполнение, это становится более болезненным.

Вот пример прокрутки к реквизиту с идентификатором.

import React, { Component } from 'react';
import { StyleSheet, Text, View, ScrollView, TouchableOpacity, Dimensions, Alert, findNodeHandle, Image } from 'react-native';
class MyCustomScrollToElement extends Component {

constructor(props) {
  super(props)
  this.state = {
  }
  this._nodes = new Map();
}
componentDidMount() {
  const data = ['First Element', 'Second Element', 'Third Element', 'Fourth Element', 'Fifth Element' ];
  data.filter((el, idx) => {
    if(el===this.props.id){
      this.scrollToElement(idx);
    }
  })

}
scrollToElement =(indexOf)=>{
  const node = this._nodes.get(indexOf);
  const position = findNodeHandle(node);
  this.myScroll.scrollTo({ x: 0, y: position, animated: true });
}
render(){
  const data = ['First Element', 'Second Element', 'Third Element', 'Fourth Element', 'Fifth Element' ];
  return (
  <View style={styles.container}>
    <ScrollView ref={(ref) => this.myScroll = ref} style={[styles.container, {flex:0.9}]} keyboardShouldPersistTaps={'handled'}>
      <View style={styles.container}>
          {data.map((elm, idx) => <View ref={ref => this._nodes.set(idx, ref)} style={{styles.element}}><Text>{elm}</Text></View>)}
      </View>
    </ScrollView>
  </View>
);

}

const styles = StyleSheet.create({
container: {
  flexGrow: 1,
  backgroundColor:"#d7eff9"
},
element:{
  width: 200,
  height: 200,
  backgroundColor: 'red'
}
});
export default MyCustomScrollToElement;

Да, это возможно, используя scrollTo Метод - см. документы. Вы можете вызвать этот метод в componentDidMount, Вам просто нужно, чтобы ссылка называлась так: this.myScroll.scrollTo(...), Обратите внимание, что если у вас есть массив элементов одного типа, вы должны использовать FlatList вместо.

Для iOS - лучший способ использовать ScrollViewс contentOffsetсвойство. Таким образом, он будет изначально отображаться в правильном положении. С помощьюscrollTo добавит дополнительный лишний рендер после первого.

Для Android - другого варианта нет scrollTo

Другие вопросы по тегам