6

Ich gebe zu, dass ich zu diesem Zeitpunkt ziemlich neu in Reactive Native bin, also nehme ich an, dass dies eine einfache Antwort und eine potentiell dumme Frage ist :) In der Tat, dies ist vielleicht kein RN Problem per se, wie ich unten erläutern werde.Daten in eine reaktive native FlatList laden

Was ich habe, ist der folgende Code:

class MyScreen extends React.Component { 

    constructor(inProps) { 
    super(inProps); 
    this.state = { listData : [ ] }; 
    } 

    render() { return (
    <View> 
     <FlatList data={ this.state.listData } 
     renderItem={ ({item}) => 
      <Text>{item.firstName} {item.lastName}</Text> 
     } 
     /> 
    </View> 
); } 

    async componentDidMount() { 
    const data = [ ]; 
    let keys = await AsyncStorage.getAllKeys(); 
    keys.forEach(async function(inKey) { 
     let obj = await AsyncStorage.getItem(inKey); 
     obj = JSON.parse(obj); 
     data.push(obj); 
    }); 
    this.setState({ listData : data }); 
    }; 

} 

Das Ziel ist einfach die Daten von AsyncStorage zu laden und es in der Liste auszugeben. Ziemlich einfach würde es scheinen, oder? Leider kann ich es nicht zum Laufen bringen: Die Liste ist leer.

Nun, um die dummen Dinge zu decken: Ja, es gibt tatsächlich Daten gespeichert und ja, es ist in der richtigen Form (Ich weiß dies, weil wenn ich einfach EINEN bestimmten Gegenstand und push() es in Daten dann erscheint es in die Liste wie erwartet).

Tatsächlich weiß ich, was das Problem ist: es ist, dass der Aufruf von setState() ausgeführt wird, bevor der Aufruf von keys.forEach() abgeschlossen wird. Ich weiß das, weil, wenn ich die setState() mit diesem ersetzen:

setTimeout(function() { 
    this.setState({ listData : data }); 
}.bind(this), 1000); 

... dann plötzlich meiner Liste aufgefüllt wird, wie erwartet (ich kann auch etwas console.log werfen() s 'da rein und sehe dass die eine nach dem keys.forEach() -Aufruf tatsächlich VOR denen innerhalb der keys.forEach()) ausgeführt wird. Ich weiß auch, dass der getItem() Aufruf offensichtlich vor dem Aufruf von JSON.parse() wartet, sonst würde das gar nicht funktionieren.

Mit anderen Worten: Ich weiß, dass dies durch die asynchrone Natur dieses Codes verursacht wird. Was ich nicht herausfinden kann ist, wie man es richtig schreibt, um dies zu vermeiden.

Ich sehe es an und ich denke "gut, keys.forEach() ist ein blockierender Anruf, also wie könnte es möglicherweise nicht vor dem Aufruf von setState() abgeschlossen, BESONDERS gegeben die asynchrone Verwendung auf den Schlüsseln .forEach() - Callback UND die Wartezeit auf den getItem() - Aufruf im Inneren? "... der einzige Gedanke, den ich hatte, war ein async vor dem keys.forEach() -Aufruf zu löschen, aber das macht keinen Unterschied .

Also scheint es eine grundlegende JavaScript-Frage im Wesentlichen zu sein, aber ich kann nicht scheinen, es zu lösen ... aber auf der anderen Seite bin ich nicht sicher, ob ich versuche, dies in einem zu machen So, dass ich grundsätzlich nicht im RN-Land sein sollte und vielleicht DAS ist das eigentliche Problem.

Beachten Sie, dass die Daten dynamisch aus AsyncStorage abgerufen werden müssen, da der Fluss durch die Anwendung fließt (das Hinzufügen erfolgt auf einem anderen Bildschirm). Daher scheint mir der Versuch, sie in componentDidMount() zu laden, sinnvoll. Ich habe gerade mit der asynchronen Art der beteiligten Anrufe aufgelegt.

Irgendwelche Vorschläge?

Danke!

EDIT: Ich sollte auch erwähnen, dass ich eine Möglichkeit, die ich lösen kann, ist, nur die asynchrone Natur vollständig zu entfernen: anstatt einzelne Datenelemente zu speichern, sondern speichern Sie alles in einem einzigen Objekt in AsyncStorage. Dann kann ich das Element ONE lesen, das ein Array wäre (oder ein Array enthalten würde), dann ist der einzige asynchrone Teil der getItem() -Aufruf, aber ich kann die Callback-Version davon verwenden und dafür sorgen, dass es funktioniert. .und ich bin nicht gegen diesen Ansatz überhaupt, aber an dieser Stelle möchte ich wirklich verstehen, was ich falsch mache, wenn aus keinem anderen Grund, als etwas zu lernen, weil dies scheint, wie es funktionieren sollte :)

+0

Ein super Hacky Weg wäre ... {data.length> 1? this.setState ({listData: data}): emptylist} ... Könnten Sie den setState auch nicht direkt nach data.push (obj) verschieben? Es wäre ineffizient, in dem Sinne, dass Sie Daten umschreiben, aber würde auch sicherstellen, dass Sie Daten haben, wenn Sie einen Fehler finden? Async Warten ist super neu für mich, also DANKE für das Posten. Das ist eine goooooooood-Reaktionsfrage. –

Antwort

3

keys.forEach führen Sie den Code in seinem eigenen Bereich (in seiner eigenen Callback-Funktion), und das bricht Ihr async/await Paar.

Tun Sie dies statt:

for (let inKey of keys) { 
    let obj = await AsyncStorage.getItem(inKey); 
    obj = JSON.parse(obj); 
    data.push(obj); 
} 

Wenn Sie wirklich forEach wollen, müssen Sie Ihre eigene Version schreiben, die Promise zurückgibt, so dass Sie await auf es zum Beispiel kann der Haken:

await keys.awaitableForEach(async function(inKey) { 
+0

Sieht perfekt aus, danke! Ich werde letztendlich mit dem Einzelobjekt-Ansatz gehen, nur weil es sowieso effizienter ist, aber zumindest verstehe ich jetzt, warum ich Probleme hatte: Ich wusste nicht, dass jeder das asynchrone/erwartete Paar zerbrechen würde ... und es gab keine Aus irgendeinem Grund, den ich forEach verwenden musste, war das erste, was mir in den Sinn kam. –

Verwandte Themen