{"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.","language":"javascript","status":"active","last_verified":"Thu Apr 23","install":{"commands":["npm install redux-immutable-state-invariant"],"cli":null},"imports":["import immutableStateInvariantMiddleware from 'redux-immutable-state-invariant';","const createImmutableStateInvariantMiddleware = require('redux-immutable-state-invariant').default;","immutableStateInvariantMiddleware({ isImmutable: (val) => typeof val !== 'object', ignore: ['users.drafts'] });"],"auth":{"required":false,"env_vars":[]},"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.","tag":null,"tag_description":null,"last_tested":null,"results":[]},"compatibility":null}