DocsReact HooksuseLoading

useLoading

useLoading is a custom hook that tracks the loading state of an effect action in Redux. It returns true when an effect action is dispatched and resets to false when the corresponding .succeeded or .failed action is dispatched. This hook is particularly useful for managing UI loading indicators tied to asynchronous operations.


Importing

To use useLoading, import it from @blue-functor/remodel:

import { useLoading } from '@blue-functor/remodel';

Parameters

ParameterTypeRequiredDescription
actionAction | ActionCreator | stringYesThe effect action to track. Accepts an action object, action creator, or action type string. The hook monitors the loading state of this action in the Redux store.

Returns

A boolean value indicating whether the specified action is currently in a loading state. Returns false by default if the action state is not found.


Example Usage

Basic Usage: Tracking with Action Creator

src/components/LoadingButton.tsx
import React from 'react';
import { useLoading } from '@blue-functor/remodel';
import { signIn } from '@/models/users/';
 
const LoadingButton: React.FC = () => {
  const isLoading = useLoading(signIn);
 
  return (
    <button disabled={isLoading}>
      {isLoading ? 'Signing In...' : 'Sign In'}
    </button>
  );
};
 
export default LoadingButton;

In this example:

  • useLoading tracks the signIn action, which is an EffectActionCreator.
  • The button is disabled and displays a loading state when the action is being processed.

Using with Action Object

src/components/UserProfile.tsx
import React from 'react';
import { useLoading } from '@blue-functor/remodel';
import { fetchUser } from '@/models/users/';
 
const UserProfile: React.FC<{ userId: string }> = ({ userId }) => {
  const dispatch = useDispatch();
  const isLoading = useLoading(fetchUser(userId));
 
  React.useEffect(() => {
    dispatch(fetchUser(userId));
  }, [userId]);
 
  if (isLoading) return <Spinner />;
  
  return <ProfileData />;
};

Using with Action Type String

src/components/DataList.tsx
import React from 'react';
import { useLoading } from '@blue-functor/remodel';
 
const DataList: React.FC = () => {
  const isLoading = useLoading('@/FETCH_DATA');
 
  return (
    <div>
      {isLoading ? <LoadingSpinner /> : <DataTable />}
    </div>
  );
};

Multiple Loading States

src/components/Dashboard.tsx
import React from 'react';
import { useLoading } from '@blue-functor/remodel';
import { fetchPosts, fetchUsers, fetchComments } from '@/models/';
 
const Dashboard: React.FC = () => {
  const postsLoading = useLoading(fetchPosts);
  const usersLoading = useLoading(fetchUsers);
  const commentsLoading = useLoading(fetchComments);
  
  const isAnyLoading = postsLoading || usersLoading || commentsLoading;
 
  return (
    <div>
      {isAnyLoading && <GlobalLoadingBar />}
      <PostsSection loading={postsLoading} />
      <UsersSection loading={usersLoading} />
      <CommentsSection loading={commentsLoading} />
    </div>
  );
};

How It Works

  1. State Subscription:

    • The hook uses useSelector to access the state.effect.loading slice in the Redux store.
    • The action.type is extracted automatically whether you pass an action creator, action object, or string.
    • Uses safe optional chaining (?.) to prevent errors if the action state doesn’t exist yet.
  2. Automatic Setup:

    • The loading state is managed by the system epics and system reducer, which are included out of the box with @blue-functor/remodel.
    • No additional setup is required. The system epics automatically track the lifecycle of effect actions and update the loading state in the Redux store.
  3. Performance Optimization:

    • The action type is memoized using useMemo to prevent unnecessary selector recreations.
    • Returns false by default instead of undefined for cleaner boolean logic.

Best Practices

Do

// Use with action creator
const isLoading = useLoading(fetchData);
 
// Combine multiple loading states
const loading = useLoading(action1) || useLoading(action2);
 
// Use for conditional rendering
if (isLoading) return <Spinner />;

Don’t

// Don't create new action objects in render
const isLoading = useLoading(fetchData(id)); // Creates new object every render
 
// Instead, use the action creator reference
const isLoading = useLoading(fetchData);

Notes

  • UI Integration: Use useLoading to conditionally render loading indicators, disable buttons, or show spinners based on the current state of asynchronous actions.

  • Type Safety: The hook accepts flexible input types while maintaining type safety through TypeScript inference.

  • React Native Compatible: Works seamlessly in both React web and React Native applications.

useLoading provides a seamless way to integrate loading states with your UI, leveraging the built-in system reducer and epics for effortless setup.