2016-08-16 29 views
1

iterieren Ich bin gerade dabei, React/Redux zu lernen, und ich bin auf ein Problem gestoßen, als ich eine einzelne Webanwendung von mir in das Framework/Paradigma umwandelte. Was ich versuche, ist, dass der Ausgangszustand der Web-App ein Array hat, das von Objekten aus einer API-Anfrage aufgefüllt werden soll. Dieses Array heißt "macht". Ich möchte dies tun, damit ich "Marken" von der API auf der ersten Seite der Website anzeigen kann, wenn es geladen wird. Dies kann in der index.js Datei unten gesehen werden:React/Redux kann nicht über Array im Zustand

import App from './App'; 
import './index.css'; 
import configureStore from './redux/store' 
import { Provider } from 'react-redux' 

let makesUrl = 'the url to the API' 
let cached = cache.get(makesUrl) 
let makes = [] 

// Future cache setup. 
if(!cached) { 
    console.log("NOT CACHED") 
} 
else { 
    console.log("CACHED") 
} 

// Get the makes via API. 
fetch(makesUrl).then((response) => { 
    // Pass JSON to the next 'then' 
    return response.json() 
}).then((json) => { 
    json.makes.forEach((make) => { 
     makes.push(make) 
    }) 
}) 

let initialState = { 
    grids: [], 
    makes: makes 
} 

let store = configureStore(initialState) 

ReactDOM.render(
    <Provider store={store}> 
    <App /> 
    </Provider>, 
    document.getElementById('root') 
); 

Die state und dispatch sind an den Stützen kartiert und weitergegeben zu den Komponenten, die sie in meinem App.js Datei als solche müssen:

import React, { Component } from 'react' 
import { connect } from 'react-redux' 
import { bindActionCreators } from 'redux' 
import './App.css' 
import Head from './components/Head' 
import Middle from './components/Middle' 
import Foot from './components/Foot' 
import actions from './redux/actions' 

class App extends Component { 

    render() { 
    return (
     <div className="App"> 
     <div> 
      <Head /> 
      <div className="middle container"> 
      <Middle actions={this.props.actions} makes={this.props.makes}/> 
      </div> 
      <Foot /> 
     </div> 
     </div> 
    ); 
    } 
} 

function mapStateToProps(state) { 
    return state 
} 

function mapDispatchToProps(dispatch) { 
    return { 
    actions: bindActionCreators(actions, dispatch) 
    } 
} 

export default connect(mapStateToProps, mapDispatchToProps)(App) 

Bei Alle Punkte, in den Chrome-Dev-Tools, kann ich sehen, dass der API-Aufruf erfolgreich war und der Zustand makes: Array[62] mit 62 Objekten im Inneren gezeigt wird, wenn ich jedoch Konsole die Länge des Arrays in der Komponente protokolliert, die diese weitergegeben werden Wie unten zu sehen, heißt es, die Länge ist 0 und jeder Index des Arrays ist nicht definiert.

import React, { Component } from 'react' 

    class MakeButtons extends Component { 

     handleClick(event) { 
      event.preventDefault() 
      console.log("CLICK") 
     } 

     render() { 
      return(
       <div> 
        { 
         console.log(this.props.makes.length) 
        } 
       </div> 
      ) 
     } 
    } 

    export default MakeButtons 

Dies ist im Wesentlichen, was ich in den letzten paar Stunden, um herauszufinden versucht habe, so kann ich die forEach oder map Funktion Links/Taste für jedes der Objekte in dem Array zurück, aber bei In dem Moment, in dem das nicht funktioniert, trotz Dev Tools, die den Zustand als normal anzeigen. Jede Hilfe/Erklärungen würde sehr geschätzt werden!

+1

Sie initialisieren die App vor dem Abruf ist zurück. Darüber hinaus sollten Sie den Init-Aufruf als Aktion/Reduzierer ausführen, der diesen Prozess * viel * sauberer macht und Ihre App nicht abhängig vom ersten Abrufanruf hängen muss. – ajmajmajma

+0

@ajmajmajma Ah das macht Sinn! Wäre die Methode componentWillMount in dieser Situation geeignet, um eine Aktion zu versenden? – sidp

+0

Ja, entweder componentWillMount oder componentDidMount, weil sie beim Laden Ihrer App nur einmal aufgerufen werden.Sie erstellen eine Aktion mit Ihrem asynchronen Aufruf, der den Status aktualisiert und ihn in einem dieser beiden Aufrufe aufruft. – ajmajmajma

Antwort

1

Sie müssen also nur eine Aktion/Reduzierung für Ihre Init einrichten, dann können Sie sie in componentWillMount oder componentDidMount aufrufen, weil sie nur einmal beim Laden Ihrer App aufgerufen werden.

In der Art und Weise Sie jetzt tun es sind, haben Sie einen Abruf und eine App die Daten aus der, dass nicht für die Asynchron-Anruf holen mit Warten zu beenden, bevor es die App gestartet wird.

Sie wollen einfach nur Ihre init Aktion erstellen, so dass Ihre Aktion Schöpfer etwas aussehen würde:

import * as services from './services'; 

    function initMyAppDispatcher(type, data, status) { 
     return { 
      type, 
      data, 
      status 
     }; 
    } 


    export function initMyApp(dispatch, makesUrl) { 
     dispatch(initMyAppDispatcher(actions.INIT_APP, {}, 'PENDING'); 

     return services.myInitCall(makesUrl) 
     .then((data) => 
      dispatch(initMyAppDispatcher(actions.INIT_APP, data, 'SUCCESS'), 
      (error) => 
      dispatch(initMyAppDispatcher(actions.INIT_APP, error, 'FAILURE'), 
     ) 
     .catch(yourErrorHandling); 
    } 

Services.myInitCall jedoch ist, dass Sie es umsetzen wollen, so stellen Sie sicher, dass Sie es zurück, als ein Versprechen exportieren . In Ihrem Fall können Sie diese Zeile durch fetch(makesUrl) ersetzen, solange Sie dort Zugriff haben. Dann hat es so eingerichtet haben, können Sie Ihre Reduzierungen wie so gesetzt:

case actions.INIT_APP: 
      if (action.status) { 
       switch (action.status) { 
        case PENDING: 
         //you can use this to create a "loading" state like a spinner or whatever 
         return state; 
        case SUCCESS: 
         // note: this is immutablejs syntax, use whatever you prefer 
         return state.set('makes', action.data); 
        case FAILURE: 
         return state; 

        default: 
         return state; 
       } 
      } 
      return state; 

Eine Sache zu beachten ist, ich habe Versand in meine Aktion Schöpfer, weil ich mapDispatchToProps anstelle von mapToProps verwenden. Also Ihr Container etwas wie folgt aussieht:

import * as actionCreators from './action-creators'; 

function mapStateToProps(state) { 

    return { 
     makes: state.get('makes') 
    }; 
} 

function mapDispatchToProps(dispatch, ownProps) { 
    return { 
     initMyApp: actionCreators.initMyApp.bind(null, dispatch) 
    }; 
} 


export default function(component = Component) { 
    return connect(mapStateToProps, mapDispatchToProps)(component); 
} 

dann in Ihrer Komponente componentWillMount oder componentDidMount, passieren in und rufen Sie Ihre init-Funktion

componentDidMount() { 
     this.props.initMyApp(); 

    } 
+0

Dies ist eine ausgezeichnete Erklärung, vielen Dank! – sidp

Verwandte Themen