2017-08-25 1 views
0

Réagissez (à partir de create-react-app) avec MobX. Utilisation d'axios pour les appels d'API backend asynchrones.Le refactoring rompt l'état initial

Ce code fonctionne. L'état initial (tableau de problèmes) est rempli, et la page Web présentant ce composant est rendue avec le contenu initial de l'état.

import { observable, computed, autorun, reaction } from 'mobx' 
import axios from 'axios' 

class IssuesStore { 
    @observable issues = [] 

    constructor() { 
    autorun(() => console.log("Autorun:" + this.buildIssues)) 

    reaction(
    () => this.issues, 
     issues => console.log("Reaction: " + issues.join(", ")) 
    ) 
    } 

    getIssues(data) { 
    return data.map((issue) => ({title: issue.name, url: issue.url, labels: issue.labels})) 
    } 

    @computed get buildIssues() { 
    const authToken = 'token ' + process.env.REACT_APP_GH_OAUTH_TOKEN 

    axios.get(`https://api.github.com/repos/${process.env.REACT_APP_GH_USER}/gh-issues-app/issues`, 
        { 'headers': {'Authorization': authToken} }) 
     .then(response => { 
     console.log(response) 
     this.issues = this.getIssues(response.data) 
     return this.issues 
     }) 
     .catch(function(response) { 
     console.log(response) 
     }) 
    } 
} 

export default IssuesStore 

Pour tenter de séparer les promesses d'invocation de l'API à partir de composants individuels et les magasins, j'ai retiré l'appel Axios dans un fichier js séparé, comme une collection de fonctions:

import axios from 'axios' 

const authToken = 'token ' + process.env.REACT_APP_GH_OAUTH_TOKEN 

export function loadIssues() { 
    return this.apiPromise(
    `https://api.github.com/repos/${process.env.REACT_APP_GH_USER}/gh-issues-app/issues`, 
    { 'headers': {'Authorization': authToken} } 
) 
} 

export function apiPromise(endpoint, options) { 
    return axios.get(endpoint, options) 
     .then((response) => { 
     // console.log("response: " + JSON.stringify(response, null, 2)) 
     return response.data.map((issue) => ({title: issue.name, url: issue.url, labels: issue.labels})) 
     }) 
     .catch(function(response) { 
     console.log(response) 
     }) 
} 

Maintenant, mon magasin ressemble à ceci:

import { observable, computed, autorun, reaction } from 'mobx' 
import * as github from '../api/github' 

class IssuesStore { 
    @observable issues = [] 


    constructor() { 
    autorun(() => console.log("Autorun:" + this.buildIssues)) 

    reaction(
    () => this.issues, 
     issues => console.log("Reaction: " + issues.join(", ")) 
    ) 
    } 

    @computed get buildIssues() { 
    this.issues = github.loadIssues().data 
    return this.issues 
    } 
} 

export default IssuesStore 

Beaucoup plus petit ... mais la page Web lance maintenant une erreur parce qu'il voit maintenant l'état initial de issues comme undefined sur le premier rendu.

Uncaught TypeError: Cannot read property 'map' of undefined

La promesse vient compléter plus tard avec succès (comme il se doit), mais il est alors trop tard. Bien sûr, je peux configurer quelques contrôles null dans mes composants de rendu pour ne pas exécuter .map ou d'autres fonctions similaires sur des variables vides ou non encore définies.

Mais pourquoi le code fonctionne-t-il sans erreurs de rendu initial avant le refactoring, et non après? Je pensais que le refactoring maintenait effectivement le même flux logique, mais je dois manquer quelque chose?

Répondre

1

Dans votre version refondus

github.loadIssues().data

va toujours être définissable car la propriété des données sur cette promesse sera toujours non défini.

Dans la version d'origine, this.issues était uniquement défini une fois que les données étaient renvoyées par l'API, donc les seules valeurs définies étaient la valeur initiale [] et le tableau rempli de la réponse API.

Dans les vôtres, les trois états sont [] -> indéfini -> et le tableau rempli.

buildIssues devrait ressembler à ceci:

@computed get buildIssues() { 
    github.loadIssues().then((data) => { 
     this.issues = data 
    }).catch((err) => { 
     // handle err. 
    }) 
} 
+0

Merci! Travaillé. Ma compréhension des promesses n'était pas assez bonne pour comprendre pourquoi j'avais besoin d'un autre '.then()' sur la promesse retournée pour obtenir mes données. J'ai supposé que le '.then()' défini dans la promesse elle-même était suffisant pour aller chercher ce dont j'avais besoin dans la réponse. – changingrainbows