2016-02-15 17 views
9

Wiem, że zostało to wcześniej zadane, zobacz na przykład here, here i here.React Native ListView: Błędny wiersz usunięty podczas usuwania

Jednak żadna z odpowiedzi/komentarzy nie jest zadowalająca. Albo każą ci sklonować dane, które nie rozwiążą problemu, albo ustawić unikalny klucz w ListView, który rozwiązuje problem, ale tworzy nowy.

Po ustawieniu unikalnego klucza na liście i zaktualizowaniu go po usunięciu cały widok jest renderowany ponownie i przewija się do góry. Użytkownik, który przewija listę w dół, aby usunąć przedmiot, oczekuje, że lista utrzyma jego pozycję.

Oto wymyślony przykład, wykorzystując to, co zakładam był poprawny sposób klonowanie danych:

var ListViewExample = React.createClass({ 

    getInitialState() { 
    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => { 
     r1 !== r2 
    }}); 
    var rows = ['row 1', 'row 2', 'row 3']; 
    return { 
     dataSource: ds.cloneWithRows(rows), 
    }; 
    }, 

    _deleteRow() { 
    var rows = ['row 1', 'row 3']; 
    this.setState({dataSource: this.state.dataSource.cloneWithRows(rows)}) 
    }, 

    renderRow(rowData, sectionID, rowID) { 
    return <TouchableOpacity onPress={this._deleteRow} 
     style={{height: 60, flex: 1, borderBottomWidth: 1}}> 
     <Text>{rowData}</Text> 
    </TouchableOpacity> 
    }, 

    render() { 
    return (
     <ListView 
     dataSource={this.state.dataSource} 
     renderRow={this.renderRow} 
     /> 
    ); 
    } 

}); 

I here widać gif go w akcji, gdzie zły rząd get usunięty. Alternatywą, aby ustawić podpórkę key ListView, usuwa się prawidłowy wiersz, ale jak już powiedziałem, spowoduje to przewinięcie listy do góry.

UPDATE

Ja już zaakceptowane odpowiedź Nader jest poniżej, ale to nie wydaje się być właściwa droga, ponieważ nie wymaga rowHasChanged na DataSource. To także nie jest zgodne z dokumentacją ListView. To nie rozwiąże mój problem więc zostawię to zaznaczone jako odpowiedź, ale mam wrażenie, że jest to obejście zamiast poprawne rozwiązanie

Kolejna aktualizacja

Dobrze jest to niewygodne, ale moim zadaniem było rowHasChanged brakuje instrukcji return. Wydaje mi się, że za dużo cukru ES6 w mojej kawie.

Podsumowując, jeśli zepsujesz swoją funkcję rowHasChanged, z jakiegoś powodu zadziała this._ds. Jeśli robić rzeczy we właściwy sposób, aby rozpocząć z was prawdopodobnie powinien użyć czegoś takiego:

this.setState({ 
    dataSource: this.state.dataSource.cloneWithRows(rows) 
}); 

podczas aktualizacji dataSource.

Pamiętaj również, że potrzebujesz nowej datablob rows. Mutowanie go na miejscu z czymś takim, jak cloneWithRows(rows.push(newRow) nie zadziała, podczas gdy cloneWithRows(rows.concat([newRow]) będzie działać.

+0

Jeśli to pomoże ktoś inny ... kluczem dla mnie robił głęboki kopię mojej liście przed klonowanie ... więc przed cloneWithRows zrobić new_rows = [... wiersze] –

Odpowiedz

5

Myślę, że problem polega na tym, że ustawiasz źródło danych na podstawie poprzedniej instancji ListView.DataSource. Przygotowałem demo tego, co mówię o here, i wstawiam poniższy przykład wraz z innym sposobem zrobienia tego.

spróbować zrobić to:

'use strict'; 

var React = require('react-native'); 
var { 
    AppRegistry, 
    StyleSheet, 
    Text, 
    View, 
    TouchableOpacity, 
    ListView 
} = React; 

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

var SampleApp = React.createClass({ 

    getInitialState() { 
    var rows = ['row 1', 'row 2', 'row 3']; 
    return { 
     dataSource: ds.cloneWithRows(rows), 
    }; 
    }, 

    _deleteRow() { 
    var rows = ['row 1', 'row 3']; 
    this.setState({dataSource: ds.cloneWithRows(rows)}) 
    }, 

    renderRow(rowData, sectionID, rowID) { 
    return <TouchableOpacity onPress={this._deleteRow} 
     style={{height: 60, flex: 1, borderBottomWidth: 1}}> 
     <Text>{rowData}</Text> 
    </TouchableOpacity> 
    }, 

    render() { 
    return (
     <ListView 
     dataSource={this.state.dataSource} 
     renderRow={this.renderRow} 
     /> 
    ); 
    } 

}); 

https://rnplay.org/apps/YGBcIA

Można również użyć rowid w renderRow zidentyfikować element, który chcesz usunąć, a następnie w funkcji usuwania, wyzerować stan DataSource.

Sprawdź przykład, który skonfigurowałem: here.Również kod jest poniżej:

'use strict'; 

var React = require('react-native'); 
var { 
    AppRegistry, 
    StyleSheet, 
    Text, 
    View, 
    TouchableOpacity, 
    ListView 
} = React; 

var rows = ['row 1', 'row 2', 'row 3']; 
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => { 
     r1 !== r2 
    }}); 

var SampleApp = React.createClass({ 

    getInitialState() { 

    return { 
     dataSource: ds.cloneWithRows([]), 
     rows: rows 
    }; 
    }, 

    componentDidMount() { 
    this.setState({ 
     dataSource: ds.cloneWithRows(this.state.rows) 
    }) 
    }, 

    _deleteRow(rowID) { 
    this.state.rows.splice(rowID, 1) 
    this.setState({ 
     dataSource: ds.cloneWithRows(this.state.rows), 
    }) 
    }, 

    renderRow(rowData, sectionID, rowID) { 
    return <TouchableOpacity onPress={() => this._deleteRow(rowID) } 
     style={{height: 60, flex: 1, borderBottomWidth: 1}}> 
     <Text>{rowData}</Text> 
    </TouchableOpacity> 
    }, 

    render() { 
    return (
     <ListView 
     dataSource={this.state.dataSource} 
     renderRow={this.renderRow} 
     /> 
    ); 
    } 

}); 
var styles = StyleSheet.create({ 
    container: { 
    flex: 1, 
    justifyContent: 'center', 
    alignItems: 'center', 
    backgroundColor: '#F5FCFF', 
    }, 
    welcome: { 
    fontSize: 28, 
    textAlign: 'center', 
    margin: 10, 
    }, 
    instructions: { 
    textAlign: 'center', 
    color: '#333333', 
    fontSize: 19, 
    marginBottom: 5, 
    }, 
}); 

AppRegistry.registerComponent('SampleApp',() => SampleApp); 
+1

Masz absolutną rację. Dzięki! Samouczek filmowy na stronie internetowej React Native oraz dokumentacja dla ListView ustawiają nowy stan tak, jak zrobiłem, z 'dataSource: this.state.dataSource.cloneWithRows', więc myślę, że nie myślałem o zrobieniu tego po swojemu przed. Jeszcze raz dzięki! –

+0

Awesome, yeah doktorzy na ListView są ok, ale oni nie są najlepsi. Obecnie pracuję nad zgłoszeniem, mam nadzieję, że uznają, że ma to sens! –

+0

Właśnie zauważyłem, że używając metody opisanej powyżej, 'rowHasChanged' nie zostanie wywołany. Możesz to sprawdzić, umieszczając tam log konsoli. To nie może być zamierzone zachowanie, prawda? I tak jak powiedziałem, React Native docs aktualizują stan za pomocą 'this.state.dataSource.cloneWithRows' zamiast z' ds.cloneWithRows' na jakimś globalnym obiekcie 'ds'. –