2015-11-23 32 views
7

Tôi đang cố gắng triển khai một cuộn vô hạn trong React Native. Dưới đây là nguồn gốc của các thành phần:React Native ListView - rowHasChanged không kích hoạt

var React = require('react-native'); 
var server = require('../server'); 
var Post = require('./Post'); 
var SwipeRefreshLayoutAndroid = require('./SwipeRefreshLayout'); 
var backEvent = null; 
var lastPostId = ""; 
var isLoadingMore = false; 
var isLoadingTop = false; 
var onEndReachedActive = false; 

var { 
    StyleSheet, 
    ListView, 
    View, 
    Text, 
    Image, 
    ProgressBarAndroid, 
    BackAndroid 
} = React; 

class Stream extends React.Component { 

constructor(props) { 
    super(props); 
    this.ds = new ListView.DataSource({ 
     rowHasChanged: (row1, row2) => { 
      console.log("rowHasChenged FIRED!!"); 
      return false; 
     } 
    }); 

    this.state = { 
     dataSource: this.ds.cloneWithRows(['loader']), 
     hasStream: false, 
     posts: [] 
    }; 

} 

componentDidMount() { 

    BackAndroid.addEventListener('hardwareBackPress',() => { 
     this.props.navigator.jumpBack(); 
     return true; 
    }.bind(this)); 

    server.getStream('', '', 15).then((res) => { 

     lastPostId = res[res.length-1].m._id; 

     this.setState({ 
      posts: res, 
      hasStream: true, 
      dataSource: this.ds.cloneWithRows(res) 
     },() => onEndReachedActive = true); 
    }) 
} 

onRefresh() { 
    var posts = this.state.posts; 
    var firstPost = posts[0].m._id; 

    console.log(this.state.dataSource._rowHasChanged); 

    isLoadingTop = true; 

    server.getStream('', firstPost, 4000) 
     .then(res => { 
      console.log(posts.length); 
      posts = res.concat(posts); 
      console.log(posts.length); 
      this.setState({ 
       dataSource: this.ds.cloneWithRows(posts), 
       posts 
      },() => { 
       this.swipeRefreshLayout && this.swipeRefreshLayout.finishRefresh(); 
       isLoadingTop = false; 
      }); 
     }).catch((err) => { 
      isLoadingTop = false; 
     }) 

} 

onEndReached(event) { 

    if(!onEndReachedActive) return; 

    if(this.state.loadingMore || this.state.isLoadingTop)return; 
    isLoadingMore = true; 
    var posts = this.state.posts; 
    server.getStream(posts[posts.length-1].m._id, '', 15) 
     .then(res => { 
      console.log('received posts'); 
      posts = posts.concat(res); 
      lastPostId = posts[posts.length-1].m._id; 
      this.setState({ 
       dataSource: this.ds.cloneWithRows(posts), 
       posts 
      },()=>isLoadingMore = false); 
     }) 
} 

renderHeader() { 
    return (
     <View style={styles.header}> 
      <Text style={styles.headerText}>Header</Text> 
     </View> 
    ) 
} 

renderRow(post) { 

    if(post === 'loader') { 
     return (
      <ProgressBarAndroid 
       styleAttr="Large" 
       style={styles.spinnerBottom}/> 
     ) 
    } 

    let hasLoader = post.m._id === lastPostId; 

    let loader = hasLoader ? 
     <ProgressBarAndroid 
      styleAttr="Large" 
      style={styles.spinnerBottom}/> : null; 

    return (
     <View> 
      <Post 
       post={post}/> 
      {loader} 
     </View> 
    ) 
} 

render() { 


    return (
       <ListView 
        style={styles.mainContainer} 
        dataSource={this.state.dataSource} 
        renderRow={this.renderRow.bind(this)} 
        onEndReached={this.onEndReached.bind(this)} 
        onEndReachedThreshold={1} 
        pageSize={15} /> 
    ); 
} 
} 

Vấn đề là bất cứ khi nào tôi thêm (hoặc thêm vào trước) dữ liệu mới, phương pháp rowHasChanged của DataSource không cháy. Nó chỉ làm lại mọi hàng, thậm chí tho không có gì thay đổi (ngoại trừ dữ liệu mới). Bất kỳ ý tưởng nào tại sao phương thức bị bỏ qua?

Trả lời

12

Edit: Vượt qua một chức năng để setstate để tránh điều kiện chủng tộc

tôi chỉ figured it out. Nếu bạn gặp vấn đề tương tự, hãy kiểm tra điểm bạn thay đổi trạng thái của mình bằng số dataSource mới. Mine là như thế này:

this.setState({ 
    dataSource: this.ds.cloneWithRows(posts) 
}); 

Thay vào đó bạn nên luôn luôn sử dụng dataSource từ trạng thái trước đó, như thế này:

this.setState(state => ({ 
    dataSource: this.state.dataSource.cloneWithRows(posts) 
})) 

Cheers!

+0

Có ai biết nếu điều này là docu mented bất cứ nơi nào trong các tài liệu React Native? Tôi không thể tìm thấy nó đề cập cụ thể và điều này có vẻ quan trọng ... –

+0

Nếu bạn đang sử dụng trạng thái hiện tại để cập nhật trạng thái, bạn có nghĩa vụ phải sử dụng biểu mẫu 'this.setState ((state) => ({dataSource: state.dataSource.cloneWithRows (posts)})); '. Thông qua setState một hàm làm giảm nguy cơ của một điều kiện chủng tộc. – jtolds

+0

@jtolds Đã chỉnh sửa, cảm ơn! –

3

điều này phù hợp với tôi, hy vọng điều này sẽ hữu ích. Tôi đã tạo một nguồn dữ liệu mới và gán dữ liệu cập nhật cho nó về thay đổi trạng thái như sau: `

var dataSource = new ListView.DataSource(
    {rowHasChanged: (r1, r2) => (r1 !== r2)}); 

    this.setState({ dataSource : dataSource.cloneWithRows(posts) }); 

Bây giờ, dữ liệu mới được gán và chế độ xem được hiển thị chính xác. Lưu ý rằng mảng bài đăng được gán hiện giữ dữ liệu cập nhật. Vẫn tự hỏi dù đó là cách tốt nhất để làm điều đó nhưng nó hoạt động!

+0

Đây không phải là câu trả lời. Mã trong câu hỏi ban đầu, Dữ liệu mới cũng được gán như bình thường và chế độ xem cũng hiển thị chính xác. Câu hỏi đặt ra là tại sao rowHasChanged không được gọi khi dữ liệu mới đến. Để tái tạo nó, bạn cần phải đặt một console.log trong rowHasChanged và vui lòng đặt một console.log trong render(). Và bạn sẽ thấy rằng việc sử dụng mã này tất cả các hàng, không có vấn đề cũ, mới, thay đổi không thay đổi, sẽ được trả lại sau khi setState được gọi. Câu trả lời đúng được trả lời bởi Rado mình dưới đây. –

1

Tôi đồng ý rằng có vẻ như để hiểu rằng bạn nên luôn sử dụng dataSource từ trạng thái trước đó.

Tuy nhiên, khi tôi setstate cách này, rowHasChanged được gọi cho tất cả các hàng, tuy nhiên, rowHasChanged luôn trả sai và không có các hàng được trả lại ??? Tại sao?

// This is callback handler that the ListView DetailView will 
    // call when a ListView item is edited 
    onChange(waypoint: Object){ 
    console.log('Callback: rowNumber= ', waypoint.rowNumber); 
    console.log('  length(m)= ', waypoint.distance.meters); 

    var itemListChanged = this.state.itemList; 
    itemListChanged[waypoint.rowNumber-1] = waypoint; 
    this.setState({ 
      dataSource: this.state.dataSource.cloneWithRows(itemListChanged), 
    }); 
    }, 

Nếu tôi setstate cách này, renderRow được gọi là cho tất cả các hàng vô điều kiện mà không rowHasChanged bao giờ được gọi. Đó là chính xác?

this.setState({ 
    dataSource: ds.cloneWithRows(itemListChanged), 
}); 

Danh sáchXem, nguồn dữ liệu và phản ứng gốc là một đường cong học tập khó khăn đến từ C#/C/C++.

+0

Có thể bạn có thể đăng hàm tạo của bạn, hàm rowHasChanged. –

+0

Một dự đoán là bạn không tạo ra "được truyền theo giá trị", nhưng "được chuyển qua tham chiếu", có nghĩa là, this.state.dataSource đã được thay đổi thành cùng một giá trị (khi chúng trỏ đến cùng một địa chỉ ram). Hãy nói rằng bạn chỉ định một quả táo một, nó làm cho táo, bạn lấy táo và đặt một quả cam để giỏ một, bạn nghĩ rằng đó là so sánh táo với màu da cam, nhưng thực sự, bạn đang so sánh những thứ trong giỏ một với giỏ a. Bạn đang so sánh cam với màu da cam. –

+0

Các giải pháp là bạn chỉ định một quả táo để giỏ b đầu tiên và sau đó khi bạn so sánh những thứ trong giỏ, đặt cam trong giỏ c. bạn cần phải có nhiều hơn 1 rổ để so sánh. Trong ngắn hạn, bạn đặt một đạo cụ trong một nhà nước, một khi đạo cụ thay đổi, nhà nước đã được thay đổi trước khi bạn gọi setState. –

0

cho ai vẫn còn có vấn đề với rowHasChanged gọi nhưng vẫn trở về sai các đoạn sau đây có thể giúp

nguồn dữ liệu được khởi tạo như thông thường:

let ds = new ListView.DataSource ({ 

    rowHasChanged: (a, b) => { 
    const changed = (a !== b) 
    return changed 
    } 

}) 

this.data = [] 

this.state = { 
    listDataSource: ds.cloneWithRows(this.data) 
} 

đây là chức năng mà sẽ cập nhật một hàng

updateRow = (row, rowId, sectionId) => { 

    // make a shallow clone from the stored data array 
    let blob = this.data.concat() 

    // modify the row, since we are using the triple equal operator, we need to make sure we are giving it a new object (new ref) 
    blob[rowId] = Object.assign({}, blob[rowId], {label: blob[rowId].label + '..cape..deh'}) 

    // tell react to update the source 
    this.setState({ 
    listDataSource: this.state.listDataSource.cloneWithRows(blob) 
    },() => { 
    // we need to update our data storage here! after the state is changed 
    this.data = blob 
    }) 

} 
Các vấn đề liên quan