3
votes

I am writing LoadingComponent which should return a Spinner when some data is loading and when it is done it should return this.props.children.

I am getting the data at componentWillMount:

class LoadingComponent extends React.Component {
    componentWillMount() {
        this.props.userActions.onUserAuthChange();
        this.props.currencyActions.getCurrencies();
    }
}

I check if one of user or currencies is loading and if so return Spinner:

 render() {
    const {loadingUser, loadingCurrencies} = this.props;

    if (loadingCurrencies || loadingUser) {
        return (
            <Spinner/>
        )
    }
    return (
        this.props.children
    )
}

Here is where I connect the state to props:

const mapStateToProps = state => ({
  user: state.user,
  loadingUser: state.loading.loadingUser,
  loadingCurrencies: state.loading.loadingCurrencies,
});

const mapDispatchToProps = dispatch => ({
  userActions: bindActionCreators(userActions, dispatch),
  currencyActions: bindActionCreators(currencyActions, dispatch),
});

const ConnectedLoading = connect(mapStateToProps, mapDispatchToProps)
(LoadingComponent);

export default withRouter(ConnectedLoading);

And the loadingReducer:

const loadingReducer = (state = initialState.loading, action) => {
  switch (action.type) {
    case actionTypes.LOADING:
        return {...state, isLoading: action.loading};
    case actionTypes.LOADING_USER:
        return {...state, loadingUser: action.loadingUser};
    case actionTypes.LOADING_CURRENCIES:
        return {...state, loadingCurrencies: action.loadingCurrencies};
    default:
        return state;
  }
};

The thing is that loadingUser and loadingCurrencies are always false. This is getCurrencies function which dispatches with true when the data starts downloading and after that dispatches with false:

export const getCurrencies = () => async dispatch => {
    try {
        dispatch(loadingCurrencies(true));
        const currencies = await get();
        dispatch(loadCurrencies(currencies.rates));
        dispatch(loadingCurrencies(false));
    } catch (e) {

    }
}

I am using the LoadingComponent at App.js:

 render() {
    return (
        <LoadingComponent>
            <div className='App'>
                <Route path='/:lang' render={props =>
                    languages.hasOwnProperty(props.match.params.lang) ?
                        <Navbar {...props}/> :
                        <Redirect to={`/${defaultLanguage}`}/>}
                />
                <Layout/>
            </div>
        </LoadingComponent>
    )
}

This is the reducer function which listens for loadCurrencies(currencies.rates):

const currencyReducer = (state = initialState.currency, action) => {
  switch (action.type) {
    case actionTypes.LOAD_CURRENCIES:
        return {...state, currencies: action.currencies};
    case actionTypes.CHANGE_CURRENCY:
        return {...state, current: action.current};
    default:
        return state;
  }
}

During debugging noticed that at the LoadingComponent the Redux Dev Tool is not active and it starts to be active at LoadingComponent's children. Actually, when I set breakpoint at the reducer the state changes... And after Redux Dev Tool starts to be active I can observe that the loading states actually had been changed false -> true -> false. I would highly appreciate if someone could help.

1
can you post the reducer that listens to loadCurrencies(currencies.rates)?Sagiv b.g
Where is the part where you connect your Loading component to the store through connect() ?zer0chain
What happens when your empty catch is caught? You should remove that whole try/catch wrapperAndy Ray

1 Answers

1
votes

I would suggest adding redux-logger to your store: https://www.npmjs.com/package/redux-logger. This will help identify the sequence of events firing.

My initial thought is that you might have a race condition between loadCurrencies and loadingCurrencies returning. If loadCurrencies is a promise, then loadingCurrencies should dispatch in a then block to indicate the promise has returned.

IMO having bool arg methods is a code smell, and since the loading state is tied to the actual load method, it makes sense to have loading be a part of the same reducer. A pattern I typically follow is:

action

export const getCurrencies = () => {
  return dispatch => {
    dispatch(requestCurrencies()) // loading: true
    return api.getCurrencies()
      .then(currencies => dispatch(receiveCurrencies(currencies)))
  }
}

export const requestCurrencies = () => ({
  type: REQUEST_CURRENCIES,
  loadingCurrencies: true
})

export const receiveCurrencies = currencies => ({
  type: RECEIVE_CURRENCIES,
  currencies,
  loadingCurrencies: false
})