web-dev-qa-db-fra.com

Comment mettre en cache les données de l'API à l'aide d'AsyncStorage React Native

Assez nouveau pour React native et ses concepts. Je joue avec RN depuis un certain temps pour créer une application pour récupérer des données API à partir de

http://jsonplaceholder.typicode.com/photos

J'ai cherché dans la documentation d'AsyncStorage pour implémenter comment je peux mettre en cache les données de l'API afin qu'à la fin de l'application, il n'ait pas à se soucier de récupérer les données du Web encore et encore, mais n'a pas réussi à implémenter il.

Ce sera génial si vous pouvez me fournir une aide/suggestion basée sur cela. J'ai inclus mon code source pour les 2 fichiers importants dans mon application, ainsi que le fichier a Test.js avec la façon dont j'essayais d'atteindre.

import React, {Component} from 'react';
import { FlatList, View, Text, AsyncStorage, ActivityIndicator } from 'react-native';
import axios from 'axios';
import GalleryDetail from './GalleryDetail';

class GalleryList extends Component {

state = { photos: []};

componentDidMount() {
    axios.get('http://jsonplaceholder.typicode.com/photos')
    .then(response => this.setState({ photos: response.data }))
    .catch((error)=> console.warn("fetch Error: ", error));
}

getPhotos = async()=> {
    try {
        photos = await AsyncStorage.getItem('GalleryPhotos');
    }
    catch (error) {
        console.error(error);
    }
}

savePhotos(){
    AsyncStorage.setItem('GalleryPhotos', this.state.photos);
    console.log('works !');
}

renderPhoto = ({item})=> {
    return <GalleryDetail photo={item}/>
}

keyExtractor = (photo, index) => photo.id;

render () {

    if(!this.state.photos){
        return <ActivityIndicator/>;
    }

    return (
            <FlatList
                data = {this.state.photos}
                keyExtractor={this.keyExtractor}
                renderItem={this.renderPhoto}
            />
    );
}
}

export default GalleryList;

et GalleryDetail liés à GalleryList-

import React, {Component} from 'react';
import { Text, View, Image } from 'react-native';
 import Card from './Card';
 import CardSection from './CardSection';

const GalleryDetail = (props)=> {
     return (
        <Card>
            <CardSection style = {styles.headerContentStyle}>
                <Image
                    style={styles.thumbnailStyle}
                    source = {{ uri: props.photo.thumbnailUrl}}/>
                <Text style= {styles.textStyle}>{props.photo.title}    </Text>
            </CardSection>
        </Card>
    );
};

const styles = {
   headerContentStyle: {
       flexDirection: 'column',
       justifyContent: 'space-around'
   },

   thumbnailStyle: {
       height: 60,
       width: 60
   },

   textStyle: {
       fontSize: 12,
       //textAlign: 'right',
       flexDirection: 'row',
       justifyContent: 'flex-end',
       flex: 1,
       flexWrap: 'wrap',
       marginLeft: 5,
       marginRight: 5,
    }
    }

    export default GalleryDetail;

Ma méthode d'essayer était la suivante: lors du lancement de l'application, il recherchera d'abord dans asyncStorage, s'il trouve les données qu'il récupère depuis async, sinon il va sur le Web, récupérant et stockant à nouveau pour une utilisation ultérieure. J'ai essayé d'implémenter un peu comme ça dans un fichier séparé car je ne voulais pas casser mon application déjà en cours d'exécution. La syntaxe cassée bizarre est

State = {
        photos: []
    }

    componentDidMount() {

        // just a variable acting to fetch data from the stored keyvalue pair

        check = AsyncStorage.getItem("PhotosKey").then((response) => {
                     this.setState({"PhotosKey": response});
                      }).done();

        if(check) {
            console.log('Data was fetched!!!!!');
            check();
        }

        else {
            console.log("Data was not fetched!");

            var Data = axios.get('http://jsonplaceholder.typicode.com/photos').
            then(response => this.setState({ photos: response.data })).
            catch((error)=> console.warn("fetch Error: ", error));



        }
    }

Merci d'avance!

7
Jatin Suneja
async componentDidMount() {
    const photoStorage = await AsyncStorage.getItem('GalleryPhotos')
    if(photoStorage) {
      try {
        const photoResp = await axios.get('http://jsonplaceholder.typicode.com/photos')
        const photoData = await JSON.stringify(photoResp.data)
        await AsyncStorage.setItem('GalleryPhotos', photoData);
      } catch(e) {
        console.warn("fetch Error: ", error)
     }
    .then(response => this.setState({ photos: response.data }))
   }
 }

plus tard

getPhotos = async()=> {
  try {
      photos = JSON.parse(await AsyncStorage.getItem('GalleryPhotos'));
  }
  catch (error) {
    console.error(error);
  }
}
11

L'approche de Subramanya est fondamentalement tout ce dont vous avez besoin pour commencer, je vais juste introduire une approche de gestion d'état avec redux-persist où vous pourrez certainement apprécier le développement de votre application.

Redux Persist est performant, facile à implémenter et facile à étendre.

Supposons que votre application soit connectée à redux et qu'elle ait implémenté une arborescence d'état assez organisée, redux-persist stocke l'intégralité de l'état de l'application avec AsyncStorage ou tout autre moteur de stockage de votre choix.

Par exemple, supposons que votre point de terminaison API renvoie une collection de photos, tout ce que vous avez à faire est de mettre à jour le magasin et vos utilisateurs peuvent s'attendre à ce que leurs données soient sécurisées et enregistrées avec redux-persist.

Je n'ai pas testé tout le code ci-dessous

Définissons d'abord le store,

import { AsyncStorage } from 'react-native';
import { createStore, compose, applyMiddleware, } from "redux";
import { persistStore } from "redux-persist";
import ReduxThunk from "redux-thunk";

import reducers from "../reducers"

const middleWare = [ReduxThunk]

const store = createStore(
  reducers, 
  {},
  compose(applyMiddleware(...middleWare))
)

// you can define more parameters, like blacklist or whitelist a reducer
// also, specify storage engine
persistStore(store, { storage: AsyncStorage });

export default store;

Au point d'entrée de votre application,

import React, { Component } from "react";
import { Provider } from "react-redux";
import Router from "./Router";
import store from './store';

export default class App extends Component {

  constructor(props) {
    super(props);
  }

  render() {
    return (
      <Provider store={store}>
        <Router />  // navigator
      </Provider>
    );
  }
}

Enfin, votre logique API.

// action creator
export storePhoto = photos => {
    return {
        type: 'STORE_PHOTOS',
        payload: photos
    }
}

// photos reducer
import { REHYDRATE } from 'redux-persist/constants';

export default (state = {}, action) => {
  switch (action.type) {
    case STORE_PHOTOS:
      return { ...state, photos: action.payload }
  // this is where `redux-persist` handles caching
    case REHYDRATE:
      var incoming = action.payload;
      if(incoming) return { ...state, ...incoming }
      return state;
    default:
      return state;
  }
};

Pour récupérer des données, vous verrez que redux résume toutes les logiques excédentaires et il n'y a plus de setItem, getItem car redux-persist le fait automatiquement pour votre déjà.

import { connect } from "react-redux";
import { storePhotos } from "./actions";

class GalleryList extends Component {

    async componentDidMount() {
        const photos = await axios.get('http://jsonplaceholder.typicode.com/photos');
        storePhoto(photos)
    }

    renderPhoto = ({ item }) => <GalleryDetail photo={item}/>

    keyExtractor = (photo, index) => photo.id;

    render () {
        return (
            <FlatList
                data = {this.props.photos}
                keyExtractor={this.keyExtractor}
                renderItem={this.renderPhoto}
            />
        );
    }
}

// pull data from photos reducer
const mapStateToProps = ({ photos }) => {
    return {
        photos: photos.photos
    }
}

export default connect(mapStateToProps, { storePhotos })(GalleryList);

Pour résumer,

  1. Installez redux-persist dans votre projet.
  2. Importez persistStore et autoRehydrate sous forme redux-persist.
  3. Ajoutez autoRehydrate à votre magasin.
  4. Passez votre boutique à persistStore.
  5. Écoutez l'action persist/REHYDRATE sur votre réducteur et remplissez l'état en conséquence.

J'espère que ma réponse vous aidera!

3
Rex Low