2016-04-13 3 views
0

Я использую redux с реактивным маршрутизатором. Я также делаю один вызов API для загрузки данных перед загрузкой маршрутов. Технически мои данные должны быть загружены сначала, тогда мой маршрут должен быть загружен. Однако я обнаружил, что сначала загружает маршрут, а затем загружает данные из API. Ниже приведены сведения о кодах.Маршрут «onEnter» загружает данные после загрузки маршрута

index.js

import React from "react"; 
import ReactDOM from "react-dom"; 
import { Provider } from 'react-redux'; 
import {ReduxRouter} from "redux-react-router"; 
import {Route} from "react-router" 

import createBrowserHistory from "history/lib/createBrowserHistory" 
import configureStore from './store'; 

import App from "./containers/App"; 
import Home from "./components/Home"; 
import Countries from "./components/Countries"; 
import {fetchData} from './actions/actions'; 

const history = new createBrowserHistory(); 
const store = configureStore(); 

function loadData() { 
    store.dispatch(fetchData('https://restcountries.eu/rest/v1/all')) 
} 

ReactDOM.render(
    <Provider store={store}> 
    <ReduxRouter> 
     <Route history={history}> 
     <Route component={App}> 
      <Route path='/' component={Home} /> 
      <Route path='/countries' component={Countries} onEnter={loadData} /> 
     </Route> 
     </Route> 
    </ReduxRouter> 
    </Provider>, 
    document.getElementById('root') 
); 

store.js

import { compose , createStore, applyMiddleware } from "redux"; 
import rootReducer from "./../reducers"; 
import thunkMiddleware from 'redux-thunk'; 
import { routerStateReducer, reduxReactRouter } from 'redux-react-router'; 
import createHistory from 'history/lib/createBrowserHistory'; 

//Create app store 
const createAppStore = compose(
    applyMiddleware(thunkMiddleware), 
    reduxReactRouter({createHistory}) 
)(createStore); 

export default function configureStore(initialState) { 
    const store = createAppStore(rootReducer, initialState); 
    return store; 
} 

reducer.js

import * as types from "./../actions/actionTypes"; 
import {combineReducers} from "redux"; 
import {routerStateReducer} from "redux-react-router"; 

function exampleReducer (state = {isLoading : false, data : [], error:false}, action = null) { 

    switch (action.type) { 
    case types.RECV_ERROR : 
     return Object.assign({}, {isLoading : false, data: action.data, error: true}); 
    case types.REQ_DATA : 
     return Object.assign({}, {isLoading : false, error: false}); 
    case types.RECV_DATA : 
     console.log("Data receive, return immutable store ") 
     return Object.assign({}, {isLoading : false, data: action.data, error: false}); 
    default: 
     return state; 
    } 

} 

const rootReducer = combineReducers({ 
    router: routerStateReducer, 
    example: exampleReducer 
}); 

export default rootReducer; 

action.js

import * as types from "./actionTypes"; 
import axios from 'axios'; 

function requestData() { 
    return {type: types.REQ_DATA} 
}; 
function receiveData(json) { 
    return{ 
    type: types.RECV_DATA, 
    data: json 
    } 
}; 

export function fetchData(url) { 

    return function(dispatch) { 
    //dispatch request for request data to start the spinner 
    dispatch(requestData()); 
    //Now make the real API call to get the data 
    return axios({ 
     url: url, 
     timeout: 20000, 
     method: 'get', 
     responseType: 'json' 
    }).then(function(response) { 
     console.log("Data is loaded"); 
     //Data is receive, now call function to aware this 
     dispatch(receiveData(response.data)); 
    }).catch(function(response){ 
     console.error("Some error happened"); 
    }); 
    } 

} 

Countries.js

import React from "react"; 
import {connect} from 'react-redux'; 
import Country from "./Country"; 

@connect(state => ({data: state.example.data})) 
export default class Countries extends React.Component { 

    constructor(props) { 
    super(props); 
    } 

    render() { 

    const {data} = this.props; 
    console.log("Countries component is loading... data "+ data); 

    console.log("Countries this.props "+ JSON.stringify(this.props)); 
    return (<div className='container'> 
     <table className='table table-bordered table-striped'> 
     <thead> 
     <tr> 
      <th>Name</th> 
      <th>Capital</th> 
      <th>Population</th> 
      <th>Domain</th> 
     </tr> 
     </thead> 
     <tbody> 
     { data ? data.map(country => { return (<Country key={country.name} country={country} />)}): null} 

     </tbody> 
     </table> 
    </div> 
    ); 
    }; 
} 

Пожалуйста, помогите.

ответ

3

Сложность в вашем случае исходит из того факта, что загрузка данных является асинхронной операцией ().

Если вы хотите, чтобы функция onEnter дождалась загрузки данных перед запуском перехода по маршруту, вы должны использовать аргумент «обратный вызов» (как указано в react-router's doc).

Но чтобы знать, когда данные были загружены, вы также должны прослушивать изменения состояния редукции в своей функции onEnter. Вы могли бы написать что-то вроде этого:

function loadData(nextState, replace, callback) { 
    let unsubscribe; 
    function onStateChanged() { 
     const state = store.getState(); 
     if (state.example.data) { 
      unsubscribe(); 
      callback(); // data is loaded: trigger the route transition 
     } 
    } 
    unsubscribe = store.subscribe(onStateChanged); 
    store.dispatch(fetchData('https://restcountries.eu/rest/v1/all')); 
} 

Довольно сложный, не так ли?

Вы должны попытаться избежать эту модели, по нескольким причинам:

  • использует дополнительный, более низкий уровень, подписку на магазин, что увеличивает сложность в приложении
  • загрузка данных, асинхронная , и может занять некоторое время; так как вы будете блокировать процесс в функции OnEnter, то ничего не появляется на экране, и это плохо UX

Вот почему вы бы лучше загрузить данные в методе componentDidMount компонента, в неблокируемом пути. Прежде чем загружать данные, просто определите начальное состояние (без данных) для компонента.

Вы найдете полезные стандартные шаблоны в Redux's real-world example, в которых есть реагирующий маршрутизатор.

+0

Спасибо, это было полезно. – joy

+1

Ну, честно говоря, вам не нужно ждать магазина Redux таким образом. 'fetchData()' уже возвращает Promise, поэтому вы можете 'store.dispatch (fetchData (...)). then (...)'. –

0

Используйте компонент ComponentMount или компонентWillMount в компоненте <App/>.
И вы можете получить данные перед маршрутизацией.

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