路线" onEnter"加载路线后加载数据

时间:2016-04-13 06:42:55

标签: reactjs react-router

我正在使用redux和react-router。我也在加载路由之前进行一次API调用来加载数据。从技术上讲,我的数据应首先加载,然后我的路线应该加载。但是我发现它首先加载路由然后它从API加载数据。以下是代码详情。

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

请帮忙。

2 个答案:

答案 0 :(得分:3)

您案例的复杂性来自于数据加载是异步操作。

如果希望onEnter函数在触发路由转换之前等待加载的数据,则应使用&#34;回调&#34;论证(如react-router's doc中所述)。

但要知道数据何时加载,您还应该在onEnter函数中监听redux状态的变化。你可以这样写:

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函数中阻止了进程,所以屏幕上没有任何内容出现并且它是不好的用户体验

这就是为什么您可以以非阻塞的方式更好地在组件的componentDidMount方法中加载数据。在接收加载的数据之前,只需为组件定义初始状态(无数据)。

您将在Redux's real-world example中找到有用的标准模式,其中包含react-router。

答案 1 :(得分:0)

<App/>组件中使用componentDidMount或componentWillMount 您可以在路由之前获取数据。