0

I have a controller view called, that needs to fetch initial data from two API endpoints. The first part is the User authentication state and the second is a list of items, that is used to populate a menu.

Controller View:

import React, { PropTypes } from 'react';
import BaseComponent from '../../Components/Base';
import Menu from '../../Components/App/Menu';
import ItemAction from '../../Actions/Items/ItemAction';
import AuthAction from '../../Actions/Auth/AuthAction';
import ItemStore from '../../Stores/Items/ItemStore';
import AuthStore from '../../Stores/Auth/AuthStore';

class App extends BaseComponent {
    constructor(props) {
        super(props);

        this.state = {};

        this._bind('getAuth', 'getItems');
    }

    componentWillMount() {
        AuthStore.addChangeListener(this.getAuth);
        ItemStore.addChangeListener(this.getItems);
    }

    componentDidMount() {
        AuthAction.check();
        ItemAction.getItems();
    }

    componentWillUnmount() {
        AuthStore.removeChangeListener(this.getAuth);
        ItemStore.removeChangeListener(this.getItems);
    }

    getAuth() {
        this.setState(AuthStore.getState());
    }

    getItems() {
        this.setState(ItemStore.getState());
    }

    render() {
        const { user, items } = this.state;

        const childrenWithProps = React.Children.map(this.props.children,
            (child) => React.cloneElement(child, {
                user: user,
                items: items
            })
        );

        return (
            <div>
                <Menu user={user} items={items}/>
                { childrenWithProps }
            </div>
        );
    }
}

App.propTypes = {
    params: PropTypes.object.isRequired,
    query: PropTypes.object
};

export default App;

ItemStore:

import AppStore from '../AppStore';
import AppDispatcher from '../../Dispatcher/AppDispatcher';
import ItemApi from '../../Utils/API/Items/ItemApi';
import ItemConstants from '../../Constants/Items/ItemConstants';

var appState = {
    items: undefined,
    isLoading: false
};


class ItemStore extends AppStore {
    constructor() {
        super();
    }

    reset() {
        appState = {};
    }

    getState() {
        return appState;
    }
}

let storeInstance = new ItemStore();

storeInstance.dispatchToken = AppDispatcher.register(payload => {
    var action = payload.action;

    switch(action.type) {
        case ItemConstants.GET_ITEMS_SUCCESS:
            appState = {
                items: action.data
            };
            break;

        case ItemConstants.GET_ITEMS_FAILURE:
            appState = {
                items: {}
            };
            break;

        default:
            return;
    }

    storeInstance.emitChange();
});

export default storeInstance;

AuthStore:

import AppStore from '../AppStore';
import AppDispatcher from '../../Dispatcher/AppDispatcher';
import AuthApi from '../../Utils/API/Auth/AuthApi';
import AuthConstants from '../../Constants/Auth/AuthConstants';

var appState = {
    user: undefined,
    isLoading: false
};


class AuthStore extends AppStore {
    constructor() {
        super();
    }

    reset() {
        appState = {};
    }

    getState() {
        return appState;
    }
}

let storeInstance = new AuthStore();

storeInstance.dispatchToken = AppDispatcher.register(payload => {
    var action = payload.action;

    switch(action.type) {
        case AuthConstants.GET_USER_SUCCESS:
            appState = {
                user: action.user
            };
            break;

        case AuthConstants.GET_USER_FAILURE:
            appState = {
                user: undefined
            };
            break;

        default:
            return;
    }

    storeInstance.emitChange();
});

export default storeInstance;

AuthAction

import AppDispatcher from '../../Dispatcher/AppDispatcher';
import AuthApi from '../../Utils/API/Auth/AuthApi';
import AuthConstants from '../../Constants/Auth/AuthConstants';
import Jwt from '../../Utils/Jwt';

var AuthAction = {
    check: function() {
        if (Jwt.tokenIsValid()) {
            AuthApi.get().then(
                function(response) {
                    AppDispatcher.handleServerAction({
                        type: AuthConstants.GET_USER_SUCCESS,
                        user: response.data
                    });
                }
            )
            .catch(
                function(error) {
                    AppDispatcher.handleServerAction({
                        type: AuthConstants.GET_USER_FAILURE
                    });
                }
            );
        } else {
            AppDispatcher.handleServerAction({
                type: AuthConstants.GET_USER_FAILURE
            });
        }
    }
};

export default AuthAction;

ItemAction

import AppDispatcher from '../../Dispatcher/AppDispatcher';
import ItemApi from '../../Utils/API/Items/AuthApi';
import ItemConstants from '../../Constants/Items/ItemConstants';

var ItemAction = {
    getItems: function() {
            ItemApi.getItems().then(
                function(response) {
                    AppDispatcher.handleServerAction({
                        type: ItemConstants.GET_ITEMS_SUCCESS,
                        items: response.data
                    });
                }
            )
            .catch(
                function(error) {
                    AppDispatcher.handleServerAction({
                        type: ItemConstants.GET_ITEMS_FAILURE
                    });
                }
            );
    }
};

export default ItemAction;

I won't include the API files, as they just do simple calls to the backend using Axios. Everything works and as far as I can say is Flux-compatible, but I just can't avoid getting the occasional error "Cannot dispatch in the middle of a dispatch".

Can anyone help out or tell me if I'm doing it right and what I can do to prevent the dispatch errors?

Martial
  • 155
  • 1
  • 2
  • 11

1 Answers1

0

I've seen some examples where code is added to avoid multiple simultaneous requests, like in this StackOverflow thread and this blog post. The first example prevents simultaneous requests to the same endpoint and the second example actually cancels pending requests when making a new one.

Community
  • 1
  • 1
Robin Venneman
  • 383
  • 1
  • 7