2017-02-03 5 views
4

Я использую ListView, чтобы отобразить список comments и, возможно, subcomments, если они есть на комментарий. Я пытаюсь прокрутить до определенного subcomment через его ref, но я не могу заставить его работать. Я использовал 3 компонента (сводилось ниже) для достижения этой цели:React Native - ListView Scroll To Nested Child Ref?

1. Комментарии

import React, { Component } from 'react' 
import { TouchableOpacity, ListView, View, Text } from 'react-native' 
import CommentRow from './commentRow' 

const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1.id !== r2.id }); 
const commentsDataSource = [ 
    {id: '1', body: 'comment 1'},{id: '2', body: 'comment 2'},{id: '3', body: 'comment 3'},{id: '4', body: 'comment 4'},{id: '5', body: 'comment 5'},{id: '6', body: 'comment 6'},{id: '7', body: 'comment 7'},{id: '8', body: 'comment 8'},{id: '9', body: 'comment 9'},{id: '10', body: 'comment 10'}, 
    {id: '11', body: 'comment 11'},{id: '12', body: 'comment 12', hasSubComments: true},{id: '13', body: 'comment 13'},{id: '14', body: 'comment 14'},{id: '15', body: 'comment 15'},{id: '16', body: 'comment 16'},{id: '17', body: 'comment 17'},{id: '18', body: 'comment 18'},{id: '19', body: 'comment 19'},{id: '20', body: 'comment 20'} 
]; 

export default class Comments extends Component { 
    constructor(props) { 
     super(props); 

     this.state = { 
     dataSource: ds.cloneWithRows(commentsDataSource) 
     }; 
    } 

    scrollToSubCommentRef(ref) { 
     this.rowz[ref].measure((ox, oy, width, height, px, py) => { 
     const offsetY = oy; 
     this.refs.mainListView.scrollTo({ y: offsetY }) 
     }); 
    } 

    render() { 
     return (
     <View> 
      <TouchableOpacity style={{backgroundColor: 'red', padding: 50}} 
           onPress={() => this.scrollToSubCommentRef('subComment_10')}> 
       <Text>Scroll to subComment_10!</Text> 
      </TouchableOpacity> 

      <ListView ref="mainListView" 
         renderRow={comment => <CommentRow comment={comment} />} 
         dataSource={this.state.dataSource} 
         enableEmptySections={true} /> 
     </View> 
    ) 
    } 
} 

2. CommentRow

import React, { Component } from 'react'; 
import { View } from 'react-native' 
import CommentListItem from './commentListItem' 

export default class CommentRow extends Component { 
    render() { 
     const comment = this.props.comment; 

     return (
     <View key={`comment_${comment.id}`} style={{overflow: 'hidden'}}> 
      <CommentListItem comment={comment} /> 
     </View> 
    ) 
    } 
} 

3. CommentListItem

import React, { Component } from 'react' 
import { View, Text } from 'react-native' 

const subComments = [ 
    {id: '1', body: 'subcomment 1'},{id: '2', body: 'subcomment 2'},{id: '3', body: 'subcomment 3'},{id: '4', body: 'subcomment 4'},{id: '5', body: 'subcomment 5'},{id: '6', body: 'subcomment 6'},{id: '7', body: 'subcomment 7'},{id: '8', body: 'subcomment 8'},{id: '9', body: 'subcomment 9'},{id: '10', body: 'subcomment 10'}, 
    {id: '11', body: 'subcomment 11'},{id: '12', body: 'subcomment 12'},{id: '13', body: 'subcomment 13'},{id: '14', body: 'subcomment 14'},{id: '15', body: 'subcomment 15'},{id: '16', body: 'subcomment 16'},{id: '17', body: 'subcomment 17'},{id: '18', body: 'subcomment 18'},{id: '19', body: 'subcomment 19'},{id: '20', body: 'subcomment 20'}, 
    {id: '21', body: 'subcomment 21'},{id: '22', body: 'subcomment 22'},{id: '23', body: 'subcomment 23'},{id: '24', body: 'subcomment 24'},{id: '25', body: 'subcomment 25'},{id: '26', body: 'subcomment 26'},{id: '27', body: 'subcomment 27'},{id: '28', body: 'subcomment 28'},{id: '29', body: 'subcomment 29'},{id: '30', body: 'subcomment 30'}, 
    {id: '31', body: 'subcomment 31'},{id: '32', body: 'subcomment 32'},{id: '33', body: 'subcomment 33'},{id: '34', body: 'subcomment 34'},{id: '35', body: 'subcomment 35'},{id: '36', body: 'subcomment 36'},{id: '37', body: 'subcomment 37'},{id: '38', body: 'subcomment 38'},{id: '39', body: 'subcomment 39'},{id: '40', body: 'subcomment 40'}, 
    {id: '41', body: 'subcomment 41'},{id: '42', body: 'subcomment 42'},{id: '43', body: 'subcomment 43'},{id: '44', body: 'subcomment 44'},{id: '45', body: 'subcomment 45'},{id: '46', body: 'subcomment 46'},{id: '47', body: 'subcomment 47'},{id: '48', body: 'subcomment 48'},{id: '49', body: 'subcomment 49'},{id: '50', body: 'subcomment 50'}, 
    {id: '51', body: 'subcomment 51'},{id: '52', body: 'subcomment 52'},{id: '53', body: 'subcomment 53'},{id: '54', body: 'subcomment 54'},{id: '55', body: 'subcomment 55'},{id: '56', body: 'subcomment 56'},{id: '57', body: 'subcomment 57'},{id: '58', body: 'subcomment 58'},{id: '59', body: 'subcomment 59'},{id: '60', body: 'subcomment 60'}, 
    {id: '61', body: 'subcomment 61'},{id: '62', body: 'subcomment 62'},{id: '63', body: 'subcomment 63'},{id: '64', body: 'subcomment 64'},{id: '65', body: 'subcomment 65'},{id: '66', body: 'subcomment 66'},{id: '67', body: 'subcomment 67'},{id: '68', body: 'subcomment 68'},{id: '69', body: 'subcomment 69'},{id: '70', body: 'subcomment 70'} 
]; 

export default class CommentListItem extends Component { 
    rowz = []; // to hold subComment refs for scroll access 

    subCommentsList =() => { 
    return subComments.map((subComment, i) => { 
     return (
     <View ref={i => this.rowz["subComment_"+subComment.id] = i} key={"subComment_"+subComment.id}> 
      <Text>{subComment.body}</Text> 
     </View> 
    ); 
    }); 
    } 

    render() { 
    const comment = this.props.comment; 

    return (
     <View> 
     <Text>{comment.body}</Text> 
     {comment.hasSubComments && this.subCommentsList()} 
     </View> 
    ) 
    } 
} 

В исходном компоненте # 1 Я попытался прокрутить до subComment через его refsubComment_10, но мера дает неопределенную ошибку. Я понимаю this.rowz не существует в #1 только в #3 где subComments карте перебирает каждый subComment и присваивает его rowz массива (я просто понял, что это не назначьте subComment_idhere к rowz массиву по некоторым причинам).

Так как мы можем исправить этот вопрос ref присвоения в #3 карте так rowz массива получает список всех subComment refs таким образом мы можем прокручивать их? И как мы можем получить TouchableOpacity с this.scrollToSubCommentRef('subComment_10') в # 1 для прокрутки mainListView до subComment_10?

UPDATE

С предоставленным решением, ref передается rowz массива успешно, но, как вы увидите, это не прокручивается до subComment_10, вместо этого она прокручивается на дно comment 10. Он должен перейти к верхней части subComment_10 так, что это самый верхний видимый subComment по щелчку TouchableHighlight:

enter image description here

+0

В этой части: subCommentsList =() => { исх = {subComment => this.rows [ "subComment _" + subComment.id] = subComment} вы можете сказать, является ли subComment в Params и реф функция subComment парам смешаться? – eden

+0

Не знаете, что вы имеете в виду, но массив 'rows' в верхней части ** # 3 ** содержит список« подкомпонентов »при назначении указанной вами функции' ref', поэтому ref 'subcomment_10' существует в массив 'rows'. – Wonka

+0

subComments.map имеет параметр с тем же именем с функцией анонимной стрелки ref. Я подозревал, что это может вызвать конфликт. Но вы уверены, что функции работают должным образом? – eden

ответ

1

ОК, я побежал отредактированный код и понял, что вы пропали без вести. Массив refz создается локально в классе CommentListItem, поэтому вы не можете получить к нему доступ из родительских классов. Однако, поскольку вы будете делать всю навигацию из родительского класса, передав массив прокрутки до самого нижнего уровня и заполнив его, будет лучший подход. Таким образом, вы не получите this.rowz не определено Ошибка и запустите свой код, как ожидалось.

export default class Comments extends Component { 
constructor(props) { 
    super(props); 
    this.rowz = [] 
    this.state = { 
    dataSource: ds.cloneWithRows(commentsDataSource) 
    }; 
} 

scrollToSubCommentRef(ref) { 
    this.rowz[ref].measure((ox, oy, width, height, px, py) => { 
    const offsetY = oy; 
    this.refs.mainListView.scrollTo({ y: offsetY }) 
    }); 
} 

render() { 
    return (
    <View> 
     <TouchableOpacity style={{backgroundColor: 'red', padding: 50}} 
          onPress={() => this.scrollToSubCommentRef('subComment_10')}> 
      <Text>Scroll to subComment_10!</Text> 
     </TouchableOpacity> 

     <ListView ref="mainListView" 
        renderRow={comment => <CommentRow refArr={this.rowz} comment={comment} />} 
        dataSource={this.state.dataSource} 
        enableEmptySections={true} /> 
    </View> 
) 
} 
} 

Вот в Комментарии класса, мы передаем массив, (это.rowz), который мы создали в конструкторе, чтобы CommentsRow класс ~

<CommentRow refArr={this.rowz} comment={comment} /> 

В CommentRow класс, мы просто передать то, что мы имели от родительского класса,

export default class CommentRow extends Component { 
    render() { 
     const comment = this.props.comment; 

     return (
     <View key={`comment_${comment.id}`} style={{overflow: 'hidden'}}> 
      <CommentListItem refArr={this.props.refArr} comment={comment} /> 
     </View> 
    ) 
    } 
} 

Прямо здесь:

<CommentListItem refArr={this.props.refArr} comment={comment} /> 

И, наконец, в CommentListItem class, чтобы заполнить наш массив, мы можем просто позвонить this.props.refArr.push()

export default class CommentListItem extends Component { 
    rowz = []; // to hold subComment refs for scroll access 

    subCommentsList =() => { 
    return subComments.map((subComment, i) => { 
     return (
     <View ref={i => this.props.refArr["subComment_"+subComment.id] = i} key={"subComment_"+subComment.id}> 
      <Text>{subComment.body}</Text> 
     </View> 
    ); 
    }); 
    } 

    render() { 
    const comment = this.props.comment; 

    return (
     <View> 
     <Text>{comment.body}</Text> 
     {comment.hasSubComments && this.subCommentsList()} 
     </View> 
    ) 
    } 
} 

Как вы можете видеть яснее здесь:

<View ref={i => this.props.refArr["subComment_"+subComment.id] = i} key={"subComment_"+subComment.id}> 

Он просто работает и прокручивается плавно при осязаемый нажатии. Я пропустил части импорта в фрагментах выше.

+0

Спасибо за решение, это на полпути (теперь '' rowz' имеет 'refs'), но позиция прокрутки неверна. Я добавил обновление к вопросу с визуальным отображением оставшейся позиции позиции прокрутки. – Wonka

+0

Можете ли вы регистрировать, что вы измеряете для каждого вложения? Также попробуйте прокрутить, скажем, sub_comment20, какая дельта расстояния, соответствует ли высота строки? Я считаю, что это то, что вы можете понять, тестируя свою функцию scrollToSubCommentRef. – eden

+0

Я считаю, что это ваша функция и измерения, которые терпят неудачу. Это может помочь: http://stackoverflow.com/questions/34750424/react-native-listview-how-to-scroll-to-a-particular-row – eden