2015-10-27 2 views
3

Я пытаюсь реализовать бесконечный прокрутки ListView в React Native. Это работает, обнаруживая, когда listview достигает нижней части его длины содержимого и запускает другой API. Когда получен ответ, новые данные добавляются к исходным данным (это делается в моем хранилище Flux). Однако в моем компоненте, я обновляю DataSource через:React Native - Infinite Scroll ListView

setState({ 
    dataSource: this.state.dataSource.cloneWithRows(Store.getLatestData()) 
}) 

Проблему я, кажется, имея это каждый раз, когда это происходит, то ListView обновляется и теряет свои позиции прокрутки (она возвращается к началу). Как я могу добавить новые данные, не потеряв текущую позицию?

Спасибо!

UPDATE

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

this._listView.getScrollResponder().scrollTo(offset, 0); 

где this._listView является рефами компоненты ListView. Тем не менее, this._listView всегда кажется нулевым, когда я обращаюсь к нему. Мое предположение заключается в том, что ссылка на listview равна нулю в течение секунды секунды во время рендеринга?

UPDATE 2

Для получения дополнительной информации:

Мой getInitialState является:

getInitialState: function() { 
    var ds = new ListView.DataSource({ 
     rowHasChanged: (r1, r2) => r1 !== r2, 
     sectionHeaderHasChanged: (s1, s2) => true 
    }); 
    return { 
     dataSource: ds.cloneWithRows(LeaderboardsStore.getLeaderboard()), 
     //some other state too 
    } 

После того, как я обнаружил пользователь прокрутил вниз, я отправился вызов API с помощью API-интерфейс Fetch, который возвращает новые данные и добавляется к исходному массиву через concat. Хранилище, в котором хранится массив (хранилище потоков), выдает событие изменения.

В моем onStoreChange обратного вызова я переназначить DataSource следующим образом:

setState({ 
    dataSource: this.state.dataSource.cloneWithRows(Store.getLatestData()) 
}) 

который обновляет мой ListView, как и ожидался (с нежелательным изменением положения прокрутки).

Мой метод визуализации выглядит (для простоты):

render:function() { 
    return (
     <ListView 
      dataSource={this.state.dataSource} 
      renderRow={this._renderRow} 
      ref={(component) => {this._listView = component;}} 
     /> 
    ); 
} 

Я попытался изменить свой onStoreChange обратный вызов:

onStoreChangeCallback: function() { 
    this._scrollOffset = this._listView.scrollProperties.offset; 
    this.setState({ 
     dataSource: this.state.dataSource.cloneWithRows(Store.getLatestData()) 
    }, function() { 
     if (this._scrollOffset !== undefined) { 
      this._listView.getScrollResponder().scroll(offset, 0); 
     } 
    }); 
} 

однако, this._listView кажется утратившими на getScrollResponder линии: (

ответ

0

Немного сложно узнать проблему, не видя больше кода, однако я предполагаю, что React Native не понимает, что вы объединили предметы в origi список. Убедитесь, что вы используете concat присоединиться элементы:

incidents = _.uniq(incidents.concat(payload.action.response), "id") IncidentsStore.emitChange(RECEIVED_EVENT)

В этом примере я использую CONCAT, а затем lodash, чтобы убедиться, что у меня нет двойников случайно.

Кроме того, ваш getLatestData должен возвращать копию исходных элементов (slice(0) простой способ сделать это), как это:

getIncidents: (state) -> 
    dataSource: state.dataSource.cloneWithRows(incidents.slice(0)) 
    loaded: true 

Надежда, что помогает, дайте мне знать, если у вас есть вопросы.

UPDATE

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

<ListView 
    dataSource={this.state.dataSource} 
    renderRow={this.renderRow} 
    style={styles.listView} 
    onEndReachedThreshold=1200 
    onEndReached={this.fetchMoreLeaders} 
/> 

В моем компоненте я добавить следующий слушатель:

componentDidMount: function() { 
    this.pageNumber = 0; 
    IncidentsStore.addLeadersReceivedListener(this.updateList); 
    return this.fetchMoreLeaders(); 
}, 

fetchMoreLeaders: function() { 
    return LeadersActions.fetchLeaders(this.pageNumber += 1); 
}, 

updateList: function() { 
    return this.setState(LeadersStore.getLeaders(this.state)); 
}, 

Мой магазин будет возвращать что-то вроде этого:

getLeaders: function(state) { 
    return { 
    dataSource: state.dataSource.cloneWithRows(leaders.slice(0)), 
    loaded: true 
    }; 
}, 

Когда новые данные приходит я был конкат, который я написал ранее с _.uniq

+0

Спасибо, что ответили. Извините, я должен был прояснить это раньше. Я добавляю новые данные с помощью concat, и я вижу, что он работает, так как я вижу новые дополнительные результаты в списке. Проблема в том, что каждый раз, когда я просматриваю нижнюю часть и добавляю новые данные, список снова обновляется до верхней части экрана. Я бы хотел, чтобы он оставался с тем же самым смещением, что и до добавления новых данных. – coldbuffet

+0

Я понимаю. Можете ли вы поделиться каким-то кодом? – eyal83

+0

Я обновил OP для получения дополнительной информации – coldbuffet

0

Я не уверен, что вы смогли r если у меня возникла такая же проблема, и я добавляю то, что хорошо работает для меня.

Прежде всего установить данные в состоянии, или вы можете использовать getInitialState

state = { 
    loading: true, 
    currentPage: 1, 
    data: { 
     collection: [] 
    } 
    } 

Затем получать необходимые данные в componentDidMount, здесь user моя служба, которая является просто оболочкой, чтобы сделать API вызовы.

componentDidMount() { 
    this.setState({loading: true},()=>{ 
     user.artists().then((data)=>{ 
      const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); 
      const dataSource = ds.cloneWithRows(data.collection); 
      this.setState({ 
      data, 
      dataSource, 
      loading: false 
      }); 
      console.log(data.last_page); 
     }).catch((err)=>{ 
      console.log(err); 
     }).done(); 
     }); 
    } 

Это, как я рендеринг моего ListView

renderPartial(datas) { 
    if (datas.length === 0) { 
     return <View/>; 
    } 
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); 
    const dataSource = ds.cloneWithRows(datas); 
    return (
     <ListView 
     enableEmptySections={true} 
     dataSource={dataSource} 
     onEndReached={()=>this.loadOtherPage()} 
     onEndReachedThreshold={400} 
     renderRow={(rowData) => this.renderContent(rowData)} 
     /> 
    ); 
    } 

onEndReached запускает функцию this.loadOtherPage, которая идет как это, здесь вы можете увидеть, что я просто нажать на новый результате в данных data.collection.push(item); Таким образом, новые данные объединяются с существующим, а порт просмотра остается в текущей позиции вместо возврата к началу.

loadOtherPage() { 
    if (this.state.currentPage !== this.state.data.last_page && !this.state.loading) { 
     this.setState({currentPage: this.state.currentPage + 1, loading: true},()=>{ 
     user.artists({page: this.state.currentPage}) 
      .then((response)=>{ 
      const data = this.state.data; 
      response.collection.map((item)=>{ 
       data.collection.push(item); 
      }); 
      this.setState({ 
       data, 
       loading: false}); 
      }).catch((err)=>{ 
      console.log(err); 
      }); 
     }); 
    } else { 
     console.log('max page reached or it is still loading'); 
    } 
    } 

Надеюсь, это поможет кому-то.