Marco Disco
Marco Disco

Reputation: 565

React Redux - Loading state too slow - how to solve it

I'm trying to create a loading state for my Redux but it looks to "slow" to get updated.

First action fetchDB => setLoading: true => once over setLoading: false

Second action fetchCat => doesn't have the time to fire it that crashes

Really simple:

set loading action:

export const setLoading = () => {
  return async (dispatch) => {
    await dispatch({ type: SET_LOADING }); // no payload by default goes to true
  };
};

set loading reducer:

import {
  FETCH_DB,
  SET_LOADING,
} from "../types"

const initalState = {
  db: [],
  loading: false,
}

export default (state = initalState, action) => {
    switch (action.type) {
      
    // this like the other cases sets loading to FALSE
    case FETCH_DB:
      return {
        ...state,
        db: action.payload,
        current: null,
        loading: false,
      }

    case FETCH_CAT_FOOD:
      
      return {
        ...state,
        food: action.payload,
        loading: false,
      }
    case FETCH_CAT_DESIGN:
      return {
        ...state,
        design: action.payload,
        loading: false,
      }

    case SET_LOADING:
      return {
        ...state,
        loading: true,
      }

    default:
      return state
  }
}

then action I use that creates the problem:

    export const fetchCat =  kindof => {
    
      return async dispatch => {

      dispatch(setLoading()) // looks like that it doesn't get fired

   const response = await axios
          .get(`http://localhost:5000/api/categories/${kindof}`)
          .then(results => results.data)
    
        try {
          await dispatch({ type: `FETCH_CAT_${kindof}`, payload: response })
        } catch (error) {
          console.log("await error", error)
        }
      }
    }

and then the file (a custom component) that creates the problem. It crashes cause categories.map is undefined. It doesn't find loading: true so the loader doesn't stop.

import React, { useState, useEffect, Fragment } from "react"

import { Spinner } from "react-bootstrap"

import { connect, useDispatch, useSelector } from "react-redux"

import CatItem from "./CatItem" // custom component

import { fetchCat, setLoading } from "../../../store/actions/appActions"

const MapCat = ({ kindof, loading, categories }) => {
  const dispatch = useDispatch()

  useEffect(() => {
    dispatch(fetchCat(kindof)) // gives the category I want to fetch
    // eslint-disable-next-line
  }, [categories])


  if (!loading) {
    return (
      <Spinner animation="border" role="status">
        <span className="sr-only">Loading...</span>
      </Spinner>
    )
  } else {
    return (
      <Fragment>
        <div>
          {categories.map(item => (
            <CatItem item={item} />
          ))}
        </div>
      </Fragment>
    )
  }
}

const mapStateToProps = (state, kindof) =>
  ({
    loading: state.appDb.loading,
    categories: state.appDb[kindof],
  })

export default connect(mapStateToProps, { fetchCat, setLoading })(MapCat)

I think that it is supposed to work like this:

loading: false (by default) => true => time to fetch => false

But doesn't look like working. Any idea?

Upvotes: 0

Views: 5121

Answers (2)

windmaomao
windmaomao

Reputation: 7661

You have quite a bit different way of calling dispatch. Let me list them out

    dispatch(fetchCat(kindof)) // gives the category I want to fetch
    await dispatch({ type: `FETCH_CAT_${kindof}`, payload: response })

You can see, await or not basically is the way you use async operation. However dispatch takes type and payload to function, which means you have to make sure what you send to dispatch is with the right object. Of course Redux does accept custom format via plugins, so maybe if you throw it a async as input, the reducer might understand it as well?

Please double check each dispatch first, for example, write a function that only dispatch one type of action. Only after you make each call working, don't move to assemble them together into a bundled call.

Upvotes: 0

gdh
gdh

Reputation: 13682

  1. Firstly setLoading needs to return a plain object with type and payload
export const setLoading = () => ({ type: SET_LOADING });
  1. In fetchCat the then is not required. Also async await for dispatch is not required.
export const fetchCat = (kindof) => {
  return (dispatch) => {
    dispatch(setLoading()); //<---this should now be ok.

    const response = await axios.get(`http://localhost:5000/api/categories/${kindof}`)
    //   .then((results) => results.data); //<----- not required as you are using await

    try {
      dispatch({ type: `FETCH_CAT_${kindof}`, payload: response.data }); //<--- use response.data ...also async/await for dispatch is not rquired.
    } catch (error) {
      console.log("await error", error);
    }
  };
};
  1. The 2nd arg of mapStateToProps is ownProps which is an object
const mapStateToProps = (state, ownProps) =>
  ({
    loading: state.appDb.loading,
    categories: state.appDb[ownProps.kindof],
  })

Upvotes: 1

Related Questions