7
votes

Réagit Native ListView - Rowhaschanged ne tire pas

J'essaie de mettre en œuvre un défilement infini dans React Native. Vous trouverez ci-dessous la source du composant:

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} />
    );
}
}


0 commentaires

4 Réponses :


12
votes

EDIT: strong> Passez une fonction pour mettrestate pour éviter les conditions de course

Je viens de le comprendre. Si vous rencontrez le même problème, vérifiez le point sur lequel vous modifiez votre état avec le nouveau DataSource code>. Le mien était comme ceci: p> xxx pré>

à la place, vous devez toujours utiliser le DataSource code> de l'état précédent, comme celui-ci: P>

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


2 commentaires

Est-ce que quelqu'un sait si cela est documenté n'importe où dans le réact de Documents autochtones? Je ne trouve pas que cela a été mentionné spécifiquement et cela semble important ...


Si vous utilisez l'état actuel pour mettre à jour l'état, vous êtes censé utiliser le formulaire this.setstate (((État) => ({DataSource: state.datasource.clonewithrows (messages)})); . Le passage d'une fonction, une fonction réduit le risque d'une condition de race.



4
votes

Cela a travaillé pour moi, j'espère que cela aide. J'ai créé une nouvelle DataSource et a attribué les données mises à jour sur l'état change comme suit: `

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

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


1 commentaires

Ce n'est pas la réponse. Le code dans la question originale, les nouvelles données sont également attribuées à la normale et la vue est également rendue correctement. La question est la raison pour laquelle Rowhaschanged n'est pas appelé lorsque de nouvelles données viennent. Pour le reproduire, vous devez mettre une console.log dans Rowhaschanged et mettre une console.log dans le rendu (). Et vous constaterez que l'utilisation de ce code toutes les lignes, peu importe la vieille, nouvelle, modifiée non modifiée, sera re-rendue après que l'instate est appelée. La réponse correcte est répondue par RADO lui-même ci-dessous.



1
votes

Je suis d'accord ici semble em> strong> pour vouloir de sens que vous devriez toujours utiliser la source de données de l'état précédent.

Quand je mets statiste de cette façon, Rowhaschanged strong> est appelé à toutes les lignes, cependant, Rowhaschanged forte> revient toujours faux forte> et aucune ligne de lignes n'est rendue ??? Pourquoi? P>

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


4 commentaires

Peut-être que vous pourriez publier votre constructeur, Rowhaschanged Fonction.


Une hypothèse est que vous n'avez pas fabriqué «passée par la valeur», mais «passée par référence», ce qui signifie que cela.state.datasource est déjà changé à la même valeur (car ils indiquent la même adresse de RAM). Disons que vous attribuez un panier Apple A, il rendit Apple, vous sortez la pomme et mettez une orange au panier A, vous pensez que cela comparait la pomme avec orange, mais en fait, vous comparez les choses dans le panier A avec le panier A. Vous êtes comparativement orange avec orange.


La solution est d'abord attribuer une pomme au panier B d'abord, puis lorsque vous comparez les choses dans le panier, mettez l'orange dans le panier c. Vous devez avoir plus de 1 panier afin de comparer. En bref, vous mettez des accessoires dans un état, une fois que les accessoires ont changé, l'état est déjà modifié avant de téléphoner à la création d'appel.


@Ed de la montagne face exactement au même problème. Avez-vous trouvé une solution?



0
votes

Pour quiconque a toujours un problème avec Rowhaschanged appelé mais retournez faux Les extraits suivants pourraient aider

La DataSource est initialisée comme d'habitude: < Pré> xxx

Voici la fonction qui mettra à jour une ligne xxx


0 commentaires