Omid Nikrah
Omid Nikrah

Reputation: 2482

loading status change after fetch data completely

I have action creator to get data from API and have another action creator for loading status and want change loading status when data completely fetched.

Now, I wrote following codes but not working good, Loading status changes to false before data fetched completely.

My ActionCreator:

export const loadingStatus = (bool) => {
    return {
        type: Constants.LOADING_STATUS,
        isLoading: bool
    };
}
const allFlashCards = (action) => {
    return{
        type: Constants.ALL_CARD,
        ...action
    }
};

export const fetchAllFlashCards = () => {
    return (dispatch) => {
        dispatch(loadingStatus(true));
        return axios.post(API.DISPLAY_ALL_CARDS)
            .then((data)=>{
                console.warn(data);
                dispatch(allFlashCards(data));
                dispatch(loadingStatus(false));
            }).catch((error)=>{
                console.warn(error)
            });
    }
};

and my Reducer:

const FlashCard = (state = [], action) => {
    switch (action.type) {
        case Constants.ADD_CARD:
            return {...state, data: action.data};
            break;
        case Constants.ALL_CARD:
            return {...state, data: action};
            break;
        default:
            return state;
    }
};

export const Loading = (status= false, action) => {
    switch (action.type) {
        case Constants.LOADING_STATUS:
            return action.isLoading;
            break;
        default:
            return status;
    }
}

and in my component:

componentDidMount() {
    this.props.fetchCards();
}

render() {
    return(
        <div>
            {this.props.loading ?
                    <Loading/> :
                    Object.keys(this.props.cards.data).map(this.renderCard)
                }
        </div>
    );
}

const mapStateToProps = (state) => ({
    cards: state.main,
    loading: state.loading
});
const mapDispatchToProps = (dispatch) => ({
    fetchCards: bindActionCreators(fetchAllFlashCards, dispatch)
});

and combineReducer is:

import { combineReducers } from 'redux';
import FlashCard , { Loading } from './FlashCard.js';
import { routerReducer } from "react-router-redux";

export default combineReducers({
    main: FlashCard,
    loading: Loading,
    routing: routerReducer
});

In my page, I have an error in console and it's: Uncaught TypeError: Cannot read property 'data' of undefined and if put my codes in timeout fixed my bug :/

What should i do?

Upvotes: 2

Views: 1071

Answers (2)

Omid Nikrah
Omid Nikrah

Reputation: 2482

Finally, I fixed my problem:

In my actionCreator change fetchAllFlashCards method to following:

export const fetchAllFlashCards = () => {
    return (dispatch) => {
        dispatch(loadingStatus(true));
        return axios.post(API.DISPLAY_ALL_CARDS)
            .then(({data})=>{
                dispatch(allFlashCards(data));
                dispatch(loadingStatus(false));
            }).catch((error)=>{
                console.warn(error)
            });
    }
};

and in reducer change FlashCard reducer to following:

const FlashCard = (state = [], action) => {
    switch (action.type) {
        case Constants.ADD_CARD:
            return {...state, data: action.data};
            break;
        case Constants.ALL_CARD:
            return {...state, data: action.data};
            break;
        default:
            return state;
    }
};

Upvotes: 0

Sergio Moura
Sergio Moura

Reputation: 4942

Your default state is wrong here:

const FlashCard = (state = [], action) => {
    switch (action.type) {
        case Constants.ADD_CARD:
            return {...state, data: action.data};
            break;
        case Constants.ALL_CARD:
            return {...state, data: action};
            break;
        default:
            return state;
    }
};

It should be an empty object {} instead of an empty array [], since you're returning objects.

This code

export const fetchAllFlashCards = () => {
    return (dispatch) => {
        dispatch(loadingStatus(true));
        return axios.post(API.DISPLAY_ALL_CARDS)
            .then((data)=>{
                console.warn(data);
                dispatch(allFlashCards(data));
                dispatch(loadingStatus(false));
            }).catch((error)=>{
                console.warn(error)
            });
    }
};

Looks completely fine. loadingStatus(false) should not be called before setting the flash cards. Your reducers and action creators are synchronous (as they should). So, nothing of note there.

I saw that you're using action.data on the Constants.ADD_CARD action case, but in your code you do not dispatch any actions with that type. Do you do it somewhere else? Maybe that's where the error is?

EDIT:

Another place that you're using .data is in your renderer: this.props.cards.data. What's the value of the state.main? How are you creating your rootReducer? It should be something like this:

const rootReducer = combineReducers({
  main: FlashCard,
  loading: Loading,
});

Are you using main there? Or maybe cards?

Upvotes: 1

Related Questions