Search code examples
reactjstypescriptreduxredux-thunk

Data is not always coming from the API


I need your help. I am using react and redux along with typescript in my app. Using the code below, I get my products and their categories. The fact is that I get my products and they look great, but the categories don't work very well: sometimes i have to reload the page several times for them to appear, and sometimes i don't. Tell me how to fix it? Thank you very much

ApiService

export const getAllCategories = () => {
    return async (dispatch: Dispatch<ProductActionModel>, getState: () => RootState) => {
        try {
            dispatch({ type: Action_types.GET_ALL_CATEGORIES });
            const response = await fetch(`${environment.baseApiUrl}/products/categories`).then(res => res.json());
            const state = getState();
            dispatch({ type: Action_types.GET_ALL_CATEGORIES_SUCCESS, payload: [...state.products.categories, ...response] });
        } catch (error) {
            dispatch({ type: Action_types.GET_ALL_CATEGORIES_ERROR, payload: 'Something went wrong'})
        }
    }
}


export const getAllProducts = () => {
    return async (dispatch: Dispatch<ProductActionModel>) => {
        try {
            dispatch({ type: Action_types.GET_ALL_PRODUCTS });
            const response = await fetch(`${environment.baseApiUrl}/products`).then(response => response.json())
            dispatch({ type: Action_types.GET_PRODUCTS_SUCCESS, payload: response.products })
        } catch (e) {
            dispatch({ type: Action_types.GET_PRODUCTS_ERROR, payload: 'Something went wrong' });
        }
    }
}

ProductReducer

const initialState: ProductsStateModel = {
    products: [],
    categories: [],
    loading: false,
    error: null
}

export const ProductReducer = (state = initialState, action: ProductActionModel): ProductsStateModel => {
    switch (action.type) {

        case Action_types.GET_ALL_PRODUCTS:
            return { loading: true, error: null, products: [], categories: [] }
        case Action_types.GET_PRODUCTS_ERROR:
            return { loading: false, error: action.payload, products: [], categories: [] }
        case Action_types.GET_PRODUCTS_SUCCESS:
            return { loading: false, error: null, products: action.payload, categories: [] }
        case Action_types.GET_ALL_CATEGORIES:
            return state;
        case Action_types.GET_ALL_CATEGORIES_ERROR:
            return state;
        case Action_types.GET_ALL_CATEGORIES_SUCCESS:
            return { ...state, categories: [...state.categories, ...action.payload] }
        default:
            return state;
    }
}

ProductList

const { products, error, loading, categories } = useTypesSelector(state => state.products);
    const dispatch: ThunkDispatch<RootState, void, ProductActionModel> = useDispatch();

    useEffect(() => {
        dispatch(getAllProducts());
        dispatch(getAllCategories())
    },[dispatch])

    console.log(categories); // sometimes fine, sometimes [], [], [categories], []

Solution

  • It seems you want to execute reducers in order. Can you do chaining like following?

    const { products, error, loading, categories } = useTypesSelector(state => state.products);
    const dispatch: ThunkDispatch<RootState, void, ProductActionModel> = useDispatch();
    
    useEffect(() => {
        dispatch(getAllProducts()).then(()=>dispatch(getAllCategories()));
    },[dispatch])
    
    console.log(categories); // sometimes fine, sometimes [], [], [categories], []
    

    Or, make one reducer containing getAllProducts() and getAllCategories().

    export const getAllCategories = () => {
    return async (dispatch: Dispatch<ProductActionModel>, getState: () => RootState) => {
        try {
            dispatch({ type: Action_types.GET_ALL_CATEGORIES });
            const response = await fetch(`${environment.baseApiUrl}/products/categories`).then(res => res.json());
            const state = getState();
            dispatch({ type: Action_types.GET_ALL_CATEGORIES_SUCCESS, payload: [...state.products.categories, ...response] });
        } catch (error) {
            dispatch({ type: Action_types.GET_ALL_CATEGORIES_ERROR, payload: 'Something went wrong'})
        }
    }}
    
    export const getAllProducts = () => {
    return async (dispatch: Dispatch<ProductActionModel>) => {
        try {
            dispatch({ type: Action_types.GET_ALL_PRODUCTS });
            const response = await fetch(`${environment.baseApiUrl}/products`).then(response => response.json())
            dispatch({ type: Action_types.GET_PRODUCTS_SUCCESS, payload: response.products })
        } catch (e) {
            dispatch({ type: Action_types.GET_PRODUCTS_ERROR, payload: 'Something went wrong' });
        }
    }}
    
    export const initialLoader= () => {
    return async (dispatch: Dispatch<ProductActionModel>, getState: () => RootState) => { // Unify above functions}}