---
id: development-only-stability-checks
title: Development-Only Stability Checks
sidebar_label: Development-Only Stability Checks
sidebar_position: 1
hide_title: true
description: Development-Only Stability Checks
---

import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
import { InternalLinks } from '@site/src/components/InternalLinks'

# Development-Only Stability Checks

Reselect includes extra checks in development mode to help catch and warn about mistakes in selector behavior.

## `inputStabilityCheck`

Due to how <InternalLinks.CascadingMemoization /> works in Reselect, it is crucial that your <InternalLinks.InputSelectors /> do not return a new reference on each run. If an <InternalLinks.InputSelectors text='input selector' /> always returns a new reference, like

```ts
state => ({ a: state.a, b: state.b })
```

or

```ts
state => state.todos.map(todo => todo.id)
```

that will cause the selector to never memoize properly.
Since this is a common mistake, we've added a development mode check to catch this. By default, <InternalLinks.CreateSelector /> will now run the <InternalLinks.InputSelectors /> twice during the first call to the selector. If the result appears to be different for the same call, it will log a warning with the arguments and the two different sets of extracted input values.

```ts
type DevModeCheckFrequency = 'always' | 'once' | 'never'
```

| Possible Values | Description                                     |
| :-------------- | :---------------------------------------------- |
| `once`          | Run only the first time the selector is called. |
| `always`        | Run every time the selector is called.          |
| `never`         | Never run the input stability check.            |

:::info

The input stability check is automatically disabled in production environments.

:::

You can configure this behavior in two ways:

### 1. Globally through `setGlobalDevModeChecks`:

A `setGlobalDevModeChecks` function is exported from Reselect, which should be called with the desired setting.

```ts
import { setGlobalDevModeChecks } from 'reselect'

// Run only the first time the selector is called. (default)
setGlobalDevModeChecks({ inputStabilityCheck: 'once' })

// Run every time the selector is called.
setGlobalDevModeChecks({ inputStabilityCheck: 'always' })

// Never run the input stability check.
setGlobalDevModeChecks({ inputStabilityCheck: 'never' })
```

### 2. Per selector by passing an `inputStabilityCheck` option directly to <InternalLinks.CreateSelector>`createSelector`</InternalLinks.CreateSelector>:

{/* START: development-only-stability-checks/inputStabilityCheck.ts */}

<Tabs
  groupId='language'
  defaultValue='ts'
  values={[
    {label: 'TypeScript', value: 'ts'},
    {label: 'JavaScript', value: 'js'},
  ]}>
  <TabItem value='ts'>

```ts title="development-only-stability-checks/inputStabilityCheck.ts"
import { createSelector } from 'reselect'

interface RootState {
  todos: { id: number; completed: boolean }[]
  alerts: { id: number; read: boolean }[]
}

// Create a selector that double-checks the results of input selectors every time it runs.
const selectCompletedTodosLength = createSelector(
  [
    // ❌ Incorrect Use Case: This input selector will not be
    // memoized properly since it always returns a new reference.
    (state: RootState) =>
      state.todos.filter(({ completed }) => completed === true)
  ],
  completedTodos => completedTodos.length,
  // Will override the global setting.
  { devModeChecks: { inputStabilityCheck: 'always' } }
)
```

  </TabItem>
  <TabItem value='js'>

```js title="development-only-stability-checks/inputStabilityCheck.js"
import { createSelector } from 'reselect'

// Create a selector that double-checks the results of input selectors every time it runs.
const selectCompletedTodosLength = createSelector(
  [
    // ❌ Incorrect Use Case: This input selector will not be
    // memoized properly since it always returns a new reference.
    state => state.todos.filter(({ completed }) => completed === true)
  ],
  completedTodos => completedTodos.length,
  // Will override the global setting.
  { devModeChecks: { inputStabilityCheck: 'always' } }
)
```

  </TabItem>
</Tabs>

{/* END: development-only-stability-checks/inputStabilityCheck.ts */}

:::warning

This will override the global input stability check set by calling `setGlobalDevModeChecks`.

:::

## `identityFunctionCheck`

When working with Reselect, it's crucial to adhere to a fundamental philosophy regarding the separation of concerns between extraction and transformation logic.

- **Extraction Logic**: This refers to operations like `state => state.todos`, which should be placed in [input selectors]. Extraction logic is responsible for retrieving or 'selecting' data from a broader state or dataset.

- **Transformation Logic**: In contrast, transformation logic, such as `todos => todos.map(({ id }) => id)`, belongs in the [result function]. This is where you manipulate, format, or transform the data extracted by the input selectors.

Most importantly, effective memoization in Reselect hinges on following these guidelines. Memoization, only functions correctly when extraction and transformation logic are properly segregated. By keeping extraction logic in input selectors and transformation logic in the result function, Reselect can efficiently determine when to reuse cached results and when to recompute them. This not only enhances performance but also ensures the consistency and predictability of your selectors.

For memoization to work as intended, it's imperative to follow both guidelines. If either is disregarded, memoization will not function properly. Consider the following example for clarity:

```ts
// ❌ Incorrect Use Case: This will not memoize correctly, and does nothing useful!
const brokenSelector = createSelector(
  // ✔️ GOOD: Contains extraction logic.
  [(state: RootState) => state.todos],
  // ❌ BAD: Does not contain transformation logic.
  todos => todos
)
```

```ts
type DevModeCheckFrequency = 'always' | 'once' | 'never'
```

| Possible Values | Description                                     |
| :-------------- | :---------------------------------------------- |
| `once`          | Run only the first time the selector is called. |
| `always`        | Run every time the selector is called.          |
| `never`         | Never run the identity function check.          |

:::info

The identity function check is automatically disabled in production environments.

:::

You can configure this behavior in two ways:

### 1. Globally through `setGlobalDevModeChecks`:

```ts
import { setGlobalDevModeChecks } from 'reselect'

// Run only the first time the selector is called. (default)
setGlobalDevModeChecks({ identityFunctionCheck: 'once' })

// Run every time the selector is called.
setGlobalDevModeChecks({ identityFunctionCheck: 'always' })

// Never run the identity function check.
setGlobalDevModeChecks({ identityFunctionCheck: 'never' })
```

### 2. Per selector by passing an `identityFunctionCheck` option directly to <InternalLinks.CreateSelector>`createSelector`</InternalLinks.CreateSelector>:

{/* START: development-only-stability-checks/identityFunctionCheck.ts */}

<Tabs
  groupId='language'
  defaultValue='ts'
  values={[
    {label: 'TypeScript', value: 'ts'},
    {label: 'JavaScript', value: 'js'},
  ]}>
  <TabItem value='ts'>

```ts title="development-only-stability-checks/identityFunctionCheck.ts"
import { createSelector } from 'reselect'

interface RootState {
  todos: { id: number; completed: boolean }[]
  alerts: { id: number; read: boolean }[]
}

// Create a selector that checks to see if the result function is an identity function.
const selectTodos = createSelector(
  // ✔️ GOOD: Contains extraction logic.
  [(state: RootState) => state.todos],
  // ❌ BAD: Does not contain transformation logic.
  todos => todos,
  // Will override the global setting.
  { devModeChecks: { identityFunctionCheck: 'always' } }
)
```

  </TabItem>
  <TabItem value='js'>

```js title="development-only-stability-checks/identityFunctionCheck.js"
import { createSelector } from 'reselect'

// Create a selector that checks to see if the result function is an identity function.
const selectTodos = createSelector(
  // ✔️ GOOD: Contains extraction logic.
  [state => state.todos],
  // ❌ BAD: Does not contain transformation logic.
  todos => todos,
  // Will override the global setting.
  { devModeChecks: { identityFunctionCheck: 'always' } }
)
```

  </TabItem>
</Tabs>

{/* END: development-only-stability-checks/identityFunctionCheck.ts */}

:::warning

This will override the global identity function check set by calling `setGlobalDevModeChecks`.

:::
