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
