DocsReact HookscreateUseModel

createUseModel

createUseModel is a factory function that creates a single hook combining both selectors and actions for a given model. Unlike createUseSelectors and createUseActions which return all selectors or actions, createUseModel lets you pick only the keys you need per component. It also automatically provides loading state for effect actions.


Importing

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

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

Parameters

ParameterTypeRequiredDescription
options.namestringYesA name for the model (used for identification).
options.selectorsRecord<string, (state: any) => any>YesAn object containing selector functions to retrieve state slices.
options.actionsRecord<string, ActionCreator>YesAn object containing action creators (regular or effect actions).

Returns

A custom React hook with two call signatures:

Selectors only:

const { mode } = useModel(['mode']);

Selectors + Actions:

const { mode, setMode } = useModel(['mode'], ['setMode']);

The returned object contains:

  • Selected state values (inferred from your selector return types)
  • Bound action creators (automatically dispatched when called)
  • {actionName}Loading boolean keys for any effect actions in the action array

Example Usage

Defining the Hook

src/models/ui/hooks.ts
import { createUseModel, createUseSelectors, createUseActions } from '@blue-functor/remodel';
import * as actions from './actions';
import * as selectors from './selectors';
 
export const useUiModel = createUseModel({ name: 'ui', selectors, actions });
 
// These still work alongside createUseModel
export const useUiSelectors = createUseSelectors(selectors);
export const useUiActions = createUseActions(actions);

Selectors Only

src/components/ModeDisplay.tsx
import React from 'react';
import { useUiModel } from '@/models/ui';
 
const ModeDisplay: React.FC = () => {
  const { mode } = useUiModel(['mode']);
 
  return <p>Current Mode: {mode ? 'Enabled' : 'Disabled'}</p>;
};
 
export default ModeDisplay;

Selectors + Actions

src/components/ModeToggle.tsx
import React from 'react';
import { useUiModel } from '@/models/ui';
 
const ModeToggle: React.FC = () => {
  const { mode, setMode, setModeEffect, setModeEffectLoading } = useUiModel(
    ['mode'],
    ['setMode', 'setModeEffect']
  );
 
  return (
    <div>
      <p>Current Mode: {mode ? 'Enabled' : 'Disabled'}</p>
      <button onClick={() => setMode(!mode)}>
        Toggle (sync)
      </button>
      <button
        onClick={() => setModeEffect({ mode: !mode })}
        disabled={setModeEffectLoading}
      >
        {setModeEffectLoading ? 'Loading...' : 'Toggle (async)'}
      </button>
    </div>
  );
};
 
export default ModeToggle;

In this example:

  • mode is a selected state value.
  • setMode is a regular action — calling it dispatches immediately.
  • setModeEffect is an effect action — it triggers an async operation.
  • setModeEffectLoading is automatically generated because setModeEffect was created with createEffectAction. It is true while the effect is in flight and false once .succeeded or .failed is dispatched.

Effect Action Loading

When you include an effect action in the actions array, the hook automatically adds a {name}Loading boolean key to the returned object:

// Given these actions:
const setMode = createAction<boolean>('SET_MODE');                    // regular action
const fetchUser = createEffectAction<string, User, Error>('FETCH');  // effect action
 
// The hook return includes:
const { mode, setMode, fetchUser, fetchUserLoading } = useModel(
  ['mode'],
  ['setMode', 'fetchUser']
);
// setMode      → no loading key (regular action)
// fetchUser    → fetchUserLoading: boolean (effect action)

Loading state is read from state.effect.loading — the same system reducer used by useLoading. No additional setup is required.


Type Safety

The hook provides full TypeScript inference:

  • Selector keys are autocompleted from the keys of your selectors object
  • Action keys are autocompleted from the keys of your actions object
  • Return types are inferred from selector return types and action creator signatures
  • Loading keys are only generated for effect actions at the type level
  • Duplicate keys are rejected at compile time
// Full autocomplete and type inference
const { mode } = useUiModel(['mode']);
//      ^? boolean (inferred from selector return type)
 
// Type errors for invalid keys
useUiModel(['nonExistent']);      // Error: not a valid selector key
useUiModel(['mode'], ['oops']);   // Error: not a valid action key
 
// Duplicate keys are caught at compile time
useUiModel(['mode', 'mode']);     // Error: duplicate key

How It Works

  1. Factory Time (runs once, outside React):

    • Pre-computes which actions are effect actions by checking for .succeeded and .failed properties.
    • Builds a map of action keys to their type strings for efficient loading state lookups.
  2. Hook Render Time:

    • Uses a single useSelector call with shallowEqual to select both state values and loading states in one pass — one subscription, one equality check.
    • Bound action creators are memoized and only recreated if dispatch changes.
    • Inline array literals (e.g., ['mode']) are stabilized with useRef to prevent unnecessary recomputations across renders.

Comparison with Other Hooks

FeaturecreateUseModelcreateUseSelectors + createUseActionswithProps
Pick specific keysYesNo (returns all)Yes
Auto loading stateYesNo (use useLoading)No
Single hook callYesTwo separate hooksN/A (HOC)
Type inferenceFullFullRequires manual Props interface
Duplicate key preventionYesN/AN/A
Works with hooksYesYesNo (class + function components)

Best Practices

Do

// Pick only what you need per component
const { mode } = useUiModel(['mode']);
 
// Combine selectors and actions in one call
const { mode, setMode } = useUiModel(['mode'], ['setMode']);
 
// Use loading state for UI feedback
const { fetchUser, fetchUserLoading } = useModel([], ['fetchUser']);

Don’t

// Don't select everything if you only need one value
const { mode, error, userName } = useUiModel(['mode', 'error', 'userName']);
// If you only use `mode`, the component re-renders when error/userName change too
 
// Don't mix createUseModel with useLoading for the same action
// createUseModel already provides loading state
const { fetchUser, fetchUserLoading } = useModel([], ['fetchUser']);
const isLoading = useLoading(fetchUser); // Redundant

Notes

  • Coexists with existing hooks: createUseModel works alongside createUseSelectors and createUseActions. You can adopt it incrementally without rewriting existing code.

  • Performance: Uses a single useSelector subscription with shallowEqual comparison, so the component only re-renders when selected values actually change.

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

By using createUseModel, you get a single hook that combines state selection, action dispatching, and loading state tracking with full type safety and minimal boilerplate.