liveBook · Manning

Loading

As explained in React Hooks in Action: With Suspense and Concurrent Mode

A loading state is component data indicating that required code, assets, or data are not yet ready for rendering.

isLoading is component state that indicates whether required data is ready for rendering. Set it to true before an asynchronous request, then render a loading indicator until the request succeeds or fails. section 4.3.2 section 4.3.1

A typical loading-state cycle is:

  • Initialize the data collection as empty and isLoading as true, so the first render shows a spinner instead of incomplete content. section 4.3.2
  • Dispatch a request action that keeps isLoading true, clears stale data, and removes any previous error. section 4.3.2
  • On success, store the returned data and set isLoading to false; on failure, store the error and also set isLoading to false. section 4.3.2
  • Render the normal interface only when there is no error and isLoading is false. section 4.3.4

Loading fetched users

A UserPicker starts with users set to null. Its useEffect calls fetch after the first render; while users remains null, it returns a Spinner. When the response is parsed and setUsers updates state, React renders the user options. A delayed JSON server response makes the loading state observable. section 4.2.3

Listing 4.8 The BookablesList component loading its own data
import {useReducer, useEffect, Fragment} from "react"; 
import {sessions, days} from "../../static.json";             #1
import {FaArrowRight} from "react-icons/fa";
import Spinner from "../UI/Spinner";
import reducer from "./reducer";
 
import getData from "../../utils/api";                        #2
 
const initialState = {
  group: "Rooms",
  bookableIndex: 0,
  hasDetails: true,
  bookables: [],                                              #3
  isLoading: true,                                            #4
  error: false                                                #4
};
 
export default function BookablesList () {
  const [state, dispatch] = useReducer(reducer, initialState); 
 
  const {group, bookableIndex, bookables} = state;
  const {hasDetails, isLoading, error} = state;               #5
 
  const bookablesInGroup = bookables.filter(b => b.group === group);
  const bookable = bookablesInGroup[bookableIndex];
  const groups = [...new Set(bookables.map(b => b.group))];
 
  useEffect(() => {
 
    dispatch({type: "FETCH_BOOKABLES_REQUEST"});              #6
 
    getData("http://localhost:3001/bookables")                #7
 
      .then(bookables => dispatch({                           #8
        type: "FETCH_BOOKABLES_SUCCESS",                      #8
        payload: bookables                                    #8
      }))
 
      .catch(error => dispatch({                              #9
        type: "FETCH_BOOKABLES_ERROR",                        #9
        payload: error                                        #9
      }));
 
  }, []);
 
  function changeGroup (e) {}
  function changeBookable (selectedIndex) {}
  function nextBookable () {}
  function toggleDetails () {}
 
  if (error) {                                                #10
    return <p>{error.message}</p>                             #10
  }                                                           #10
 
  if (isLoading) {                                            #11
    return <p><Spinner/> Loading bookables...</p>             #11
  }                                                           #11
 
  return ( /* unchanged UI for bookables and details */ );
}
#1 No longer import bookables.
#2 Import the getData function.
#3 Set bookables to an empty array.
#4 Add the new properties to the initial state.
#5 Destructure the new properties from state.
#6 Dispatch an action for the start of the data fetching.
#7 Fetch the data.
#8 Save the loaded bookables in state.
#9 Update state with any error.
#10 Return some simple error UI if there’s an error.
#11 Return some simple loading UI while waiting for data.

Inspect the complete BookablesList implementation, including reducer state, asynchronous loading, error handling, and conditional rendering. section 4.3.4

<Suspense fallback={<MySpinner />}>
  <MyFirstComponent />
  <MySecondComponent />
</Suspense>

Compare local isLoading state with Suspense fallbacks, which manage loading placeholders for suspended components or groups of components. section 1.4.2

How React Hooks in Action: With Suspense and Concurrent Mode covers it

How deep it goes
Explains initial, request, success, and error transitions through reducer actions, while stopping short of request-cancellation internals.
Examples
Uses UserPicker, BookablesList, Spinner, delayed JSON responses, and FETCH_BOOKABLES reducer actions as concrete demonstrations.
What it assumes
Assumes React components, hooks, state updates, promises, and JSX, while demonstrating loading-state design through progressively built fetching examples.
What you can do after
Build it from scratch by coordinating asynchronous requests, reducer actions, and conditional React rendering for loading and error states.
How it connects
Connects useEffect, useReducer, fetch API, error handling, and Suspense through alternative loading-state patterns.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of React Hooks in Action: With Suspense and Concurrent Mode. The section links point to the parts of the book it draws on.