CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-reselect

Selectors for Redux - memoized functions for computing derived data from state.

Pending
Overview
Eval results
Files

development.mddocs/

Development Mode Utilities

Development-only stability checks and debugging utilities to help identify common selector issues and ensure optimal performance.

Capabilities

setGlobalDevModeChecks

Configures global development mode check settings for all selectors in the application.

/**
 * Overrides the development mode checks settings for all selectors
 * @param devModeChecks - Partial configuration object for dev mode checks
 */
function setGlobalDevModeChecks(devModeChecks: Partial<DevModeChecks>): void;

interface DevModeChecks {
  /** Check for unstable input selector results */
  inputStabilityCheck: DevModeCheckFrequency;
  
  /** Check if result function is an identity function */
  identityFunctionCheck: DevModeCheckFrequency;
}

type DevModeCheckFrequency = 'once' | 'always' | 'never';

Basic Usage:

import { setGlobalDevModeChecks } from "reselect";

// Run checks only on first selector call (default)
setGlobalDevModeChecks({ 
  inputStabilityCheck: 'once',
  identityFunctionCheck: 'once' 
});

// Run checks on every selector call
setGlobalDevModeChecks({ 
  inputStabilityCheck: 'always',
  identityFunctionCheck: 'always' 
});

// Disable specific checks
setGlobalDevModeChecks({ 
  inputStabilityCheck: 'never',
  identityFunctionCheck: 'once' 
});

// Partial configuration (unspecified settings retain current values)
setGlobalDevModeChecks({ 
  inputStabilityCheck: 'always' 
  // identityFunctionCheck keeps its current setting
});

Development Mode Checks Overview

Development mode checks help identify common selector performance issues and mistakes.

Input Stability Check:

  • Detects when input selectors return different references for the same inputs
  • Helps identify unnecessary recomputations due to unstable input selectors
  • Warns when input selectors create new objects/arrays on each call

Identity Function Check:

  • Detects when the result function just returns its first argument unchanged
  • Helps identify selectors that don't add value and could be simplified
  • Warns about potential performance issues with unnecessary selector layers

Per-Selector Configuration

Override global settings for specific selectors using CreateSelectorOptions.

import { createSelector } from "reselect";

// Override global settings for specific selector
const selectSpecialData = createSelector(
  [selectInputData],
  (data) => processData(data),
  {
    devModeChecks: {
      inputStabilityCheck: 'always', // Always check this selector
      identityFunctionCheck: 'never'  // Never check identity for this one
    }
  }
);

// Disable all checks for performance-critical selector
const selectCriticalPath = createSelector(
  [selectLargeDataset],
  (data) => criticalProcessing(data),
  {
    devModeChecks: {
      inputStabilityCheck: 'never',
      identityFunctionCheck: 'never'
    }
  }
);

Common Issues and Solutions

Unstable Input Selectors:

import { createSelector } from "reselect";

// ❌ BAD: Creates new array on every call
const selectBadItems = (state) => state.items.filter(item => item.active);

// ✅ GOOD: Stable input selector
const selectItems = (state) => state.items;
const selectActiveFilter = (state) => true; // or from actual filter state

const selectActiveItems = createSelector(
  [selectItems, selectActiveFilter],
  (items, shouldFilterActive) => 
    shouldFilterActive ? items.filter(item => item.active) : items
);

Identity Function Issues:

import { createSelector } from "reselect";

// ❌ BAD: Identity function - just returns first argument
const selectBadUser = createSelector(
  [(state) => state.user],
  (user) => user // This is an identity function
);

// ✅ GOOD: Actually transforms the data
const selectUserDisplayName = createSelector(
  [(state) => state.user],
  (user) => user ? `${user.firstName} ${user.lastName}` : 'Anonymous'
);

// ✅ ALSO GOOD: Direct selector when no transformation needed
const selectUser = (state) => state.user;

DevModeChecksExecutionInfo

Information about development mode check execution (internal type).

interface DevModeChecksExecutionInfo {
  /** Information about input stability check execution */
  inputStabilityCheck: DevModeCheckExecutionInfo;
  
  /** Information about identity function check execution */
  identityFunctionCheck: DevModeCheckExecutionInfo;
}

interface DevModeCheckExecutionInfo {
  /** Whether the check should run */
  shouldRun: boolean;
  
  /** Number of times this check has been executed */
  timesRun: number;
}

Production Behavior

Development mode checks are automatically disabled in production builds to ensure optimal performance.

// Development mode checks only run when process.env.NODE_ENV !== 'production'
// In production, these checks are completely bypassed for performance

import { createSelector, setGlobalDevModeChecks } from "reselect";

// This configuration only affects development builds
setGlobalDevModeChecks({ inputStabilityCheck: 'always' });

const selectData = createSelector(
  [selectInput],
  (input) => processInput(input)
  // Dev mode checks run in development, ignored in production
);

Debugging and Monitoring

Checking Recomputations:

import { createSelector } from "reselect";

const selectExpensiveData = createSelector(
  [selectLargeDataset, selectFilters],
  (data, filters) => expensiveProcessing(data, filters)
);

// Monitor recomputations in development
console.log('Recomputations:', selectExpensiveData.recomputations());

// Use the selector
const result = selectExpensiveData(state);
console.log('After use:', selectExpensiveData.recomputations());

// Reset counter for testing
selectExpensiveData.resetRecomputations();

Custom Development Logging:

import { createSelector } from "reselect";

const selectWithLogging = createSelector(
  [selectInput],
  (input) => {
    if (process.env.NODE_ENV === 'development') {
      console.log('Selector recomputing with input:', input);
    }
    return processInput(input);
  }
);

Integration with Testing

import { createSelector, setGlobalDevModeChecks } from "reselect";

describe('Selector Tests', () => {
  beforeEach(() => {
    // Enable all checks for testing
    setGlobalDevModeChecks({
      inputStabilityCheck: 'always',
      identityFunctionCheck: 'always'
    });
  });

  it('should not recompute with same inputs', () => {
    const selector = createSelector(
      [(state) => state.data],
      (data) => processData(data)
    );

    selector(state1);
    const recomputations1 = selector.recomputations();
    
    selector(state1); // Same state
    const recomputations2 = selector.recomputations();
    
    expect(recomputations2).toBe(recomputations1); // Should not increase
  });
});

Types

interface DevModeChecks {
  inputStabilityCheck: DevModeCheckFrequency;
  identityFunctionCheck: DevModeCheckFrequency;
}

type DevModeCheckFrequency = 'once' | 'always' | 'never';

interface DevModeChecksExecutionInfo {
  inputStabilityCheck: DevModeCheckExecutionInfo;
  identityFunctionCheck: DevModeCheckExecutionInfo;
}

interface DevModeCheckExecutionInfo {
  shouldRun: boolean;
  timesRun: number;
}

Install with Tessl CLI

npx tessl i tessl/npm-reselect

docs

development.md

index.md

memoization.md

selector-creation.md

selector-creator.md

structured-selectors.md

tile.json