redux-thunk: Pause Component Execution Until Action Creator Completes

筅森魡賤 提交于 2019-12-23 12:00:03

问题


I've been struggling with this problem for several weeks now. I'm finally throwing in the towel and asking for help on this because I'm clearly not doing something right. I have a React.js app that is using redux and redux-thunk. I'm simply trying to get my Component Container to initiate the loading of data, but not render until the data comes back from the fetch request. Seems simple enough I know. Here is what I've done:

Container Component

'use strict';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchActivePlayer } from '../actions/index';
import PlayerDetails from '../components/players/player-detail';
import Spinner from '../components/common/spinner/index';
import store from '../store';

export default class PlayerDetailContainer extends Component {
    constructor(props) {
        super(props);
    }

    componentWillMount() {
        this.props.fetchActivePlayer(this.props.params.player_slug)
    }

    render() {
        if (!this.props.activePlayer.activePlayer) {
            return (
                <Spinner text="Loading..." style="fa fa-spinner fa-spin" />
            );
        }

        return (
            <PlayerDetails 
                player={ this.props.activePlayer.activePlayer } 
            />
        );
    }
}

function mapStateToProps(state) {
    return {
        activePlayer: state.activePlayer
    }
}
export default connect(mapStateToProps, { fetchActivePlayer })(PlayerDetailContainer);

Action Creator

export function fetchActivePlayer(slug) {
    return (dispatch, getState) => {
        return axios.get(`${ROOT_URL}/players/${slug}`)
        .then(response => {
            dispatch({
                type: FETCH_ACTIVE_PLAYER,
                payload: response
            })
        })
        .catch(err => {
            console.error("Failure: ", err);
        });    
    };
}

Store

'use strict';
import React from 'react';
import { browserHistory } from 'react-router';
import { createStore, applyMiddleware } from 'redux';
import { routerMiddleware } from 'react-router-redux';
import thunk from 'redux-thunk';
import promise from 'redux-promise';
import reducers from './components/reducers/index';

const createStoreWithMiddleware = applyMiddleware(
    thunk,
    promise,
    routerMiddleware(browserHistory)
) (createStore);
export default createStoreWithMiddleware(reducers);

Routes

export default (
<Route path="/" component={ App }>
        <IndexRoute component={ HomePage } />
        <Route path="players/:player_slug" component={ PlayerContainer } />
        <Route path="/:player_slug" component={ PlayerContainer } />
    </Route>
);

Here are the versions I'm using for everything: react = 0.14.7 react-redux = 4.4.1 redux-thunk = 0.5.3

When I run this, I don't receive any errors but it's clear that my action creator is firing but my component container continues instead of waiting for the creator to finish. Like I said, I'm sure I must be missing something really simple but I can't seem to figure out what that is.

Thank you in advance. Any help would be greatly appreciated.


回答1:


  1. your action(fetch) in componentWillMount is async ,component will not wait.
  2. Usually when you fetch some data , you want to know about statuses of fetching process. like "isfetching" in order to showing loader, success and failure in order to showing error.
  3. You can use these statuses to not load/mount/launch component, Until Action Creator Completes.

Thus, you should organize your redux parts somthing like that:

state

activePlayer:{
    data:data,
    isFetching: true/false,
    error:""
    }

action

export const fetchActivePlayer = slug => dispatch =>{
    dispatch({
        type: 'FETCH_ACTIVE_PLAYER_REQUEST',
        isFetching:true,
        error:null
    });

    return axios.get(`${ROOT_URL}/players/${slug}`)
    .then(response => {
        dispatch({
            type: 'FETCH_ACTIVE_PLAYER_SUCCESS',
            isFetching:false,
            payload: response
        });
    })
    .catch(err => {
        dispatch({
            type: 'FETCH_ACTIVE_PLAYER_FAILURE',
            isFetching:false,
            error:err
        });
        console.error("Failure: ", err);
    });

};

reducer

const initialState = {data:null,isFetching: false,error:null};
export const actionPlayer = (state = initialState, action)=>{
    switch (action.type) {
        case 'FETCH_ACTIVE_PLAYER_REQUEST':
        case 'FETCH_ACTIVE_PLAYER_FAILURE':
        return { ...state, isFetching: action.isFetching, error: action.error };

        case 'FETCH_ACTIVE_PLAYER_SUCCESS':
        return { ...state, data: action.payload, isFetching: action.isFetching,
                 error: null };
        default:return state;

    }
};

then your component might look like this (hardcode)

class PlayerDetailContainer extends Component {
    componentWillMount() {
        this.props.fetchActivePlayer(this.props.params.player_slug)
    }
    render() {
        if (this.props.isFetching) {
            return <Spinner text="Loading..." style="fa fa-spinner fa-spin" />

        }else if (this.props.error) {
            return <div>ERROR {this.props.error}</div>
        }else {
            return <PlayerDetails  player={ this.props.data }  />
        }
    }
}
const mapStateToProps = state =>({
        isFetching: state.activePlayer.isFetching,
        data: state.activePlayer.data,
        error: state.activePlayer.error,
})

I don't know how your app looks like. Target of this example is to illustrate approach.



来源:https://stackoverflow.com/questions/38064688/redux-thunk-pause-component-execution-until-action-creator-completes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!