2016-08-16 3 views
1

Я изучаю React/Redux, и у меня возникла проблема при преобразовании единственного веб-приложения моей страницы в рамки/парадигму. То, что я пытаюсь сделать, это позволить исходному состоянию веб-приложения иметь массив, который должен быть заполнен объектами из запроса API, этот массив называется «делает». Я хочу сделать это, чтобы я мог отображать «make» из API на первой странице веб-сайта после его загрузки. Это можно увидеть в index.js файле ниже:React/Redux не может перебирать массив в состоянии

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') 
); 

state и dispatch сопоставляются с реквизитом и передается вниз к компонентам, которые нуждаются в них в моем App.js файл как таковой:

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) 

В все точки, в инструментах chrome dev, я вижу, что вызов API был успешным, и показано, что состояние имеет makes: Array[62] с 62 объектами внутри, однако, если я консольный журнал, длина массива в компоненте, который эти данные передаются, как видно ниже, он говорит, что длина равна 0 , и каждый индекс массива undefinded.

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 

Это, по сути, что я пытался выяснить, в течение последних нескольких часов, так что я могу использовать функцию forEach или map вернуть ссылки/кнопки для каждого из объектов в массиве, однако при в тот момент, когда это не работает, несмотря на то, что инструменты, показывающие состояние, являются нормальными. Любая помощь/объяснения были бы очень признательны!

+1

Вы инициализируете приложение, прежде чем принести это обратно. Кроме того, вы должны сделать init-вызов действием/редуктором, что сделает этот процесс * много * чище, и ваше приложение не будет зависать на основе первого вызова извлечения. – ajmajmajma

+0

@ajmajmajma А это имеет смысл! Может ли метод componentWillMount быть подходящим в этой ситуации для отправки действия? – sidp

+0

Да, либо componentWillMount, либо componentDidMount, потому что они вызываются только один раз при загрузке вашего приложения.Вы создаете действие с вашим асинхронным вызовом, который обновляет состояние и вызывает его в одном из этих двух. – ajmajmajma

ответ

1

Так что вам просто нужно настроить действие/редуктор для вашего init, тогда вы можете позвонить ему в componentWillMount или componentDidMount, потому что они вызывается только один раз при загрузке вашего приложения.

Так вы делаете это сейчас, у вас есть выборка и приложение, использующее данные из выборки, которая равна , а не, ожидая завершения асинхронного вызова до того, как он запустит приложение.

Вы просто хотите создать свое действие инициализации, поэтому ваши действия создателя будет что-то вроде:

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 однако вы хотите реализовать это, просто убедитесь, что вы экспортируете его обратно, как обещание , В вашем случае вы можете заменить эту строку fetch(makesUrl), если у вас есть к ней доступ. Тогда иметь его настроить, как это, вы можете установить восстановителей, как так:

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; 

Одна вещь, чтобы отметить, у меня есть отправка в моих создателей действий, потому что я использую mapDispatchToProps вместо mapToProps. Так что ваш контейнер выглядит примерно так:

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); 
} 

затем в компоненте componentWillMount или componentDidMount, передать и вызвать вашего инициализационной функцию

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

    } 
+0

Это отличное объяснение, спасибо вам большое! – sidp

Смежные вопросы