{"id":17903,"library":"redux-immutable-state-invariant","title":"Redux Immutable State Invariant Middleware","description":"Redux-immutable-state-invariant is a specialized Redux middleware designed exclusively for development environments. Its core function is to detect unintended mutations of the Redux state, both within a reducer's dispatch cycle and between dispatches. This is crucial for upholding the immutability principle central to Redux, preventing subtle bugs that arise from direct state modification rather than returning new state objects. The current stable version is 2.1.0, with a release cadence that has seen significant updates around v1 and v2, and more recently a minor bug fix in v2.1.0, indicating ongoing maintenance. Unlike libraries like Immutable.js which provide immutable data structures, this middleware acts as a runtime check for native JavaScript objects and arrays, throwing descriptive errors when mutations are detected. It is explicitly warned against using in production due to its performance overhead caused by extensive object copying for comparison.","status":"active","version":"2.1.0","language":"javascript","source_language":"en","source_url":"https://github.com/leoasis/redux-immutable-state-invariant","tags":["javascript"],"install":[{"cmd":"npm install redux-immutable-state-invariant","lang":"bash","label":"npm"},{"cmd":"yarn add redux-immutable-state-invariant","lang":"bash","label":"yarn"},{"cmd":"pnpm add redux-immutable-state-invariant","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"The primary export is a default export, which is a factory function to create the middleware.","wrong":"import { immutableStateInvariantMiddleware } from 'redux-immutable-state-invariant';","symbol":"immutableStateInvariantMiddleware","correct":"import immutableStateInvariantMiddleware from 'redux-immutable-state-invariant';"},{"note":"Since v2.0.0, CommonJS `require` must access the `.default` property due to Babel 6 transpilation. The module exports a factory function, conventionally named like `create...`.","wrong":"const createImmutableStateInvariantMiddleware = require('redux-immutable-state-invariant');","symbol":"createImmutableStateInvariantMiddleware","correct":"const createImmutableStateInvariantMiddleware = require('redux-immutable-state-invariant').default;"},{"note":"Since v2.0.0, the middleware factory accepts a single `options` object with properties like `isImmutable` and `ignore`.","wrong":"immutableStateInvariantMiddleware((val) => typeof val !== 'object');","symbol":"MiddlewareFactoryWithOptions","correct":"immutableStateInvariantMiddleware({ isImmutable: (val) => typeof val !== 'object', ignore: ['users.drafts'] });"}],"quickstart":{"code":"import { applyMiddleware, combineReducers, createStore } from 'redux';\nimport thunk from 'redux-thunk';\nimport immutableStateInvariantMiddleware from 'redux-immutable-state-invariant';\n\nconst initialState = {\n  counter: 0,\n  user: { name: 'Alice', age: 30, address: { street: 'Main St' } }\n};\n\nfunction counterReducer(state = initialState.counter, action) {\n  switch (action.type) {\n    case 'INCREMENT':\n      return state + 1;\n    case 'DECREMENT':\n      // BAD: Mutates state directly, will be caught by middleware\n      // state--;\n      return state - 1;\n    default:\n      return state;\n  }\n}\n\nfunction userReducer(state = initialState.user, action) {\n  switch (action.type) {\n    case 'SET_USERNAME':\n      // BAD: Mutates state directly, will be caught by middleware\n      // state.name = action.payload;\n\n      // GOOD: Returns new state object\n      return { ...state, name: action.payload };\n    case 'SET_ADDRESS_STREET':\n      // BAD: Deep mutation, will be caught\n      // state.address.street = action.payload;\n      \n      // GOOD: Returns new nested state objects\n      return { ...state, address: { ...state.address, street: action.payload } };\n    default:\n      return state;\n  }\n}\n\nconst rootReducer = combineReducers({\n  counter: counterReducer,\n  user: userReducer\n});\n\n// Configure middleware to ignore specific paths or custom immutability checks\nconst middlewareConfig = {\n  ignore: ['user.address.zipCode'], // Example: ignore a specific path\n  isImmutable: (value) => {\n    // Custom check: treat anything with a '__immutable' property as immutable\n    if (typeof value === 'object' && value !== null && value.__immutable) {\n      return true;\n    }\n    // Default check for primitives\n    return typeof value !== 'object' || value === null;\n  }\n};\n\nconst middleware = process.env.NODE_ENV !== 'production' ?\n  [immutableStateInvariantMiddleware(middlewareConfig), thunk] :\n  [thunk];\n\nconst store = createStore(\n  rootReducer,\n  applyMiddleware(...middleware)\n);\n\nconsole.log('Initial state:', store.getState());\n\nstore.dispatch({ type: 'INCREMENT' });\nconsole.log('State after INCREMENT:', store.getState());\n\nstore.dispatch({ type: 'SET_USERNAME', payload: 'Bob' });\nconsole.log('State after SET_USERNAME:', store.getState());\n\n// Intentionally trigger a mutation (uncomment to see the error in development)\n// store.dispatch({\n//   type: 'MUTATE_COUNTER_BADLY',\n//   payload: store.getState().counter++\n// });\n","lang":"javascript","description":"This example demonstrates how to integrate `redux-immutable-state-invariant` into a Redux store, showing both correct immutable updates and how the middleware catches direct state mutations in reducers. It also illustrates how to configure the middleware with `ignore` paths and a custom `isImmutable` function, while emphasizing its development-only usage."},"warnings":[{"fix":"Change `const createMiddleware = require('redux-immutable-state-invariant');` to `const createMiddleware = require('redux-immutable-state-invariant').default;`.","message":"For CommonJS environments, `require('redux-immutable-state-invariant')` no longer directly returns the middleware factory. You must now access the `.default` property.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"If you were passing `isImmutable` directly, wrap it in an object: `immutableStateInvariantMiddleware(myIsImmutableFn)` becomes `immutableStateInvariantMiddleware({ isImmutable: myIsImmutableFn })`. The `options` object also supports an `ignore` property for paths.","message":"The middleware factory function (the default export) now accepts a single `options` object as its argument, rather than direct arguments like `isImmutable`.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Always conditionally apply this middleware based on `process.env.NODE_ENV` or similar environment variables, ensuring it is only included when `NODE_ENV !== 'production'`.","message":"This middleware is strictly for development environments. Using it in production will severely degrade application performance due to extensive deep copying and comparison of state objects required for mutation detection.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-23T00:00:00.000Z","next_check":"2026-07-22T00:00:00.000Z","problems":[{"fix":"Modify your `require` statement to `const createImmutableStateInvariantMiddleware = require('redux-immutable-state-invariant').default;`.","cause":"Attempting to use `require('redux-immutable-state-invariant')` in a CommonJS environment without accessing the `.default` property for versions 2.0.0 and above.","error":"TypeError: (0 , _reduxImmutableStateInvariant2.default) is not a function"},{"fix":"Ensure all Redux reducers and async logic that modifies state adhere to immutability. Use spread syntax (`...`) for objects and arrays, or immutable helper libraries, to create new instances of state for any changes. Avoid direct assignments like `state.property = value` or `state.array.push(item)`.","cause":"Directly modifying a Redux state object or array property in a reducer or an asynchronous action after the initial state snapshot, rather than returning a new, modified copy.","error":"Uncaught Error: A state mutation was detected between dispatches. Previous state was { ... }, Next state is { ... }"}],"ecosystem":"npm","meta_description":null,"install_score":null,"install_tag":null,"quickstart_score":null,"quickstart_tag":null}