Skip to main content

Loading

The loading plugin for Rematch. Adds automated loading indicators for effects, so you don't need to manage state like loading: true by yourself. Inspired by dva-loading.

Compatibility​

Install the correct version of loading plugin based on the version of the core Rematch library in your project.

@rematch/core@rematch/loading
1.x.x1.x.x
2.x.x2.x.x

Install​

bash
npm install @rematch/loading
bash
npm install @rematch/loading

loadingPlugin([config])​

The loading plugin accepts one optional argument - config, which is an object with the following properties:

  • [name] (string?): key for the loading model in your store. If you name it "custom", loading state can be accessed from state.custom. Defaults to loading.
  • [asNumber] (boolean?): loading plugin by default keeps track of running effects using booleans, so for example: state.loading.global === true. You can change that behaviour and use numbers instead - plugin will keep track of the number of times an effect was executed, for example: state.loading.global === 5. Defaults to false. Deprecated, use type instead
  • [type] ("number"|"boolean"|"full"): Loading plugin by default keeps track of running effects using booleans, but sometimes you want to track if the effect promise is resolved to an Error, or loading, or if the promise is resolved correctly, in that case you can use full. If you want to track the number of times an effect was executed, you can use number instead.
  • [whitelist] (string[]?): an array of effects names that you want to use loading plugin for. If defined, plugin will work only for the whitelisted effects.
  • [blacklist] (string[]?): an array of effects names that you don't want to use loading plugin for. If defined, plugin will work for all effects except those blacklisted.

Both blacklist and whitelist accept the "full" effect name, in a format modelName/effectFunctionName, for example 'count/addOne'.

If both blacklist and whitelist aren't provided, plugin works for all effects.

Usage​

Let's say we have a model 'count' in our store. Loading plugin's state will have the following format:

json
{
"global": true, // true when any effect in any model is loading
"models": {
"count": true // true when any effect in 'count' model is loading
},
"effects": {
"count": {
"addOne": true // true when effect 'addOne' in model 'count' is loading
}
}
}
json
{
"global": true, // true when any effect in any model is loading
"models": {
"count": true // true when any effect in 'count' model is loading
},
"effects": {
"count": {
"addOne": true // true when effect 'addOne' in model 'count' is loading
}
}
}

Check out below an example of how to use loading plugin in React:

Set up your store with default or custom settings.

Setup the store​

store.ts
ts
// @filename: store.ts
import loadingPlugin, { ExtraModelsFromLoading } from "@rematch/loading"
import { init, RematchDispatch, RematchRootState } from "@rematch/core"
import { models, RootModel } from "./models"
 
type FullModel = ExtraModelsFromLoading<RootModel>
 
export const store = init<RootModel, FullModel>({
models,
plugins: [loadingPlugin()],
})
 
export type Store = typeof store
export type Dispatch = RematchDispatch<RootModel>
export type RootState = RematchRootState<RootModel, FullModel>
store.ts
ts
// @filename: store.ts
import loadingPlugin, { ExtraModelsFromLoading } from "@rematch/loading"
import { init, RematchDispatch, RematchRootState } from "@rematch/core"
import { models, RootModel } from "./models"
 
type FullModel = ExtraModelsFromLoading<RootModel>
 
export const store = init<RootModel, FullModel>({
models,
plugins: [loadingPlugin()],
})
 
export type Store = typeof store
export type Dispatch = RematchDispatch<RootModel>
export type RootState = RematchRootState<RootModel, FullModel>

If you want to use the loadingPlugin with numbers instead of booleans, you can also change the typings:

ts
// @filename: storeAsNumber.ts
import loadingPlugin, { ExtraModelsFromLoading } from "@rematch/loading"
import { init, RematchDispatch, RematchRootState } from "@rematch/core"
import { models, RootModel } from "./models"
 
type FullModel = ExtraModelsFromLoading<RootModel, { type: 'number' }>
 
export const store = init<RootModel, FullModel>({
models,
plugins: [loadingPlugin({ type: 'number' })],
})
 
export type Store = typeof store
export type Dispatch = RematchDispatch<RootModel>
export type RootState = RematchRootState<RootModel, FullModel>
ts
// @filename: storeAsNumber.ts
import loadingPlugin, { ExtraModelsFromLoading } from "@rematch/loading"
import { init, RematchDispatch, RematchRootState } from "@rematch/core"
import { models, RootModel } from "./models"
 
type FullModel = ExtraModelsFromLoading<RootModel, { type: 'number' }>
 
export const store = init<RootModel, FullModel>({
models,
plugins: [loadingPlugin({ type: 'number' })],
})
 
export type Store = typeof store
export type Dispatch = RematchDispatch<RootModel>
export type RootState = RematchRootState<RootModel, FullModel>

If you want to use the loadingPlugin with detailed Errors and Success information instead of booleans, you can also change the typings:

ts
// @filename: storeAsFull.ts
import loadingPlugin, { ExtraModelsFromLoading } from "@rematch/loading"
import { init, RematchDispatch, RematchRootState } from "@rematch/core"
import { models, RootModel } from "./models"
 
type FullModel = ExtraModelsFromLoading<RootModel, { type: 'full' }>
 
export const store = init<RootModel, FullModel>({
models,
plugins: [loadingPlugin({ type: 'full' })],
})
 
export type Store = typeof store
export type Dispatch = RematchDispatch<RootModel>
export type RootState = RematchRootState<RootModel, FullModel>
ts
// @filename: storeAsFull.ts
import loadingPlugin, { ExtraModelsFromLoading } from "@rematch/loading"
import { init, RematchDispatch, RematchRootState } from "@rematch/core"
import { models, RootModel } from "./models"
 
type FullModel = ExtraModelsFromLoading<RootModel, { type: 'full' }>
 
export const store = init<RootModel, FullModel>({
models,
plugins: [loadingPlugin({ type: 'full' })],
})
 
export type Store = typeof store
export type Dispatch = RematchDispatch<RootModel>
export type RootState = RematchRootState<RootModel, FullModel>

React usage​

Use state created by the loading plugin in your view.

Default​

tsx
// @filename: appTemplate.tsx
import React from 'react'
import { useSelector } from 'react-redux'
import { RootState } from './store'
 
export const App = () => {
const isCountLoading = useSelector(
(rootState: RootState) => rootState.loading.models.count
)
if (isCountLoading) return <div>LOADING...</div>
 
return <div>Data succesfully loaded</div>
}
tsx
// @filename: appTemplate.tsx
import React from 'react'
import { useSelector } from 'react-redux'
import { RootState } from './store'
 
export const App = () => {
const isCountLoading = useSelector(
(rootState: RootState) => rootState.loading.models.count
)
if (isCountLoading) return <div>LOADING...</div>
 
return <div>Data succesfully loaded</div>
}

Full​

tsx
// @filename: appTemplate.tsx
import React from 'react'
import { useSelector } from 'react-redux'
import { RootState } from './storeAsFull'
 
export const App = () => {
const { loading, success, error } = useSelector(
(rootState: RootState) => rootState.loading.models.count
)
if (loading) return <div>LOADING...</div>
if (error) return <div>{(error as Error).name}</div>
 
return <div>Data succesfully loaded</div>
}
tsx
// @filename: appTemplate.tsx
import React from 'react'
import { useSelector } from 'react-redux'
import { RootState } from './storeAsFull'
 
export const App = () => {
const { loading, success, error } = useSelector(
(rootState: RootState) => rootState.loading.models.count
)
if (loading) return <div>LOADING...</div>
if (error) return <div>{(error as Error).name}</div>
 
return <div>Data succesfully loaded</div>
}

Number​

tsx
// @filename: appTemplate.tsx
import React from 'react'
import { useSelector } from 'react-redux'
import { RootState } from './storeAsNumber'
 
export const App = () => {
const countCalledTimes = useSelector(
(rootState: RootState) => rootState.loading.models.count
)
if (countCalledTimes > 0) return <div>LOADING...</div>
 
return <div>Data succesfully loaded</div>
}
tsx
// @filename: appTemplate.tsx
import React from 'react'
import { useSelector } from 'react-redux'
import { RootState } from './storeAsNumber'
 
export const App = () => {
const countCalledTimes = useSelector(
(rootState: RootState) => rootState.loading.models.count
)
if (countCalledTimes > 0) return <div>LOADING...</div>
 
return <div>Data succesfully loaded</div>
}