refx Redux Middleware for Side Effects

raw JSON →
3.1.1 verified Thu Apr 23 auth: no javascript abandoned

refx is a Redux middleware designed for managing side effects in applications, last updated in 2018 with version 3.1.1. It offers an alternative to solutions like `redux-thunk` by allowing developers to define side effects as an object of action type keys, with handlers that receive both the dispatched action and the store instance. A core differentiator is its emphasis on keeping Redux actions as plain objects, promoting easier extension and composition compared to thunk-augmented action creators. Despite its unique approach and lightweight footprint (241 bytes gzipped and minified), the package has seen no updates in several years, making its compatibility with recent Redux ecosystem changes, such as Redux Toolkit 2.0 or Redux 5.0, questionable. Its release cadence is effectively non-existent, and it does not ship with TypeScript types, requiring manual declarations for modern TypeScript projects.

error Error: Middleware is not a function
cause Attempting to use `refx` in an environment that expects a different module format (e.g., CommonJS `require` in an ESM-only context, or vice-versa), or an incorrect import statement.
fix
Ensure you are using the correct import syntax for your module environment. For ESM, use import refx from 'refx';. For CommonJS, use const refx = require('refx');. Verify your bundler configuration (Webpack, Rollup, Vite, etc.) is correctly resolving refx's module exports.
error Property 'dispatch' does not exist on type 'unknown'
cause This TypeScript error can occur when using `refx` with modern Redux (v5.0+) due to stricter typing of middleware parameters. The `store` object passed to effect handlers might be typed as `any` or `unknown` in `refx`'s unofficial types, clashing with Redux's evolved type definitions.
fix
In your custom refx.d.ts or directly within your effect handlers, explicitly type the store parameter to Redux.Store or a more specific store type, ensuring dispatch is recognized. For example: (action: any, store: Redux.Store) => { store.dispatch(...) }
error console.warn: `createStore` has been deprecated. Use `configureStore` from Redux Toolkit instead.
cause This is a deprecation warning from Redux itself when `createStore` is used. While not a runtime error, it indicates using a legacy API.
fix
Refactor your Redux store initialization to use configureStore from @reduxjs/toolkit. You will need to define your refx middleware and effects within configureStore's middleware array. This may require adapting refx's integration if it expects specific createStore behaviors.
breaking The `refx` package has not been updated since 2018 (v3.1.1) and is effectively abandoned. It is highly unlikely to be compatible with modern Redux ecosystems, including Redux Toolkit 2.0 or Redux core 5.0, without significant modifications. These newer versions introduced breaking changes in middleware typing and packaging.
fix Consider migrating to actively maintained Redux side effect libraries such as Redux Thunk (v3.0+) or Redux Saga, especially when using Redux Toolkit. If sticking with `refx`, be prepared for manual compatibility layers and potential runtime issues with modern Redux versions and bundlers.
deprecated The example usage of `createStore` is deprecated in Redux 4.2.0+ and Redux 5.0+, which encourages migration to `configureStore` from Redux Toolkit. While `createStore` still works, it will show a strikethrough in IDEs.
fix Migrate your Redux store setup to use `configureStore` from `@reduxjs/toolkit`. This is the recommended modern approach for Redux applications. If `refx` cannot be integrated directly, you might need to find an alternative side effect solution.
gotcha The package does not ship with TypeScript type definitions. Using `refx` in a TypeScript project will require creating custom declaration files (`.d.ts`) for the middleware and effect handlers to ensure type safety.
fix Create a `refx.d.ts` file in your project to declare the module and its types. For example: `declare module 'refx' { import { Middleware } from 'redux'; type EffectHandler = (action: any, store: any) => any; type EffectsMap = { [key: string]: EffectHandler | EffectHandler[] }; function refx(effects: EffectsMap | EffectsMap[]): Middleware; export default refx; }`
gotcha Modern Redux (v5.0, Redux Toolkit 2.0) introduced changes to how middleware parameters (`action`, `next`) are typed, shifting them to `unknown` for enhanced safety. An older middleware like `refx` might not account for these explicit `unknown` types, potentially leading to TypeScript errors or runtime type mismatches if not carefully managed.
fix If using TypeScript, ensure your custom `refx.d.ts` definitions correctly cast or narrow the `action` and `store` parameters within your effect handlers. For JavaScript, be aware of the potential for runtime type-related issues if `refx` expects specific types that are no longer guaranteed by newer Redux versions.
npm install refx
yarn add refx
pnpm add refx

This example demonstrates how to set up `refx` middleware with a basic Redux store. It defines an effect to intercept a `TODO_ADD` action, simulate an asynchronous API call, and then dispatch a `TODO_SAVED` action once the 'save' operation completes.

import { createStore, applyMiddleware } from 'redux';
import refx from 'refx';

// A placeholder Redux reducer
function todos(state = [], action) {
  switch (action.type) {
    case 'TODO_ADD':
      return [...state, { id: Date.now(), text: action.todo, completed: false }];
    case 'TODO_SAVED':
      console.log('TODO_SAVED action dispatched:', action.todo);
      return state.map(todo => (todo.id === action.todo.id ? { ...todo, text: action.todo.text } : todo));
    default:
      return state;
  }
}

const effects = {
  TODO_ADD: (action, store) => {
    console.log(`Intercepted TODO_ADD for todo: ${action.todo}`);
    // Simulate an API call
    new Promise(resolve => setTimeout(() => resolve({ id: Date.now(), text: action.todo + ' (saved)' }), 500))
      .then(todo => {
        store.dispatch({
          type: 'TODO_SAVED',
          todo
        });
      });
  },
  // You can also add other effects, e.g., for error handling or logging
  // TODO_ERROR: (action, store) => console.error('Error:', action.error)
};

function configureStore() {
  return createStore(todos, [], applyMiddleware(refx(effects)));
}

const store = configureStore();

console.log('Initial state:', store.getState());

store.dispatch({ type: 'TODO_ADD', todo: 'Learn refx' });
store.dispatch({ type: 'TODO_ADD', todo: 'Understand side effects' });

// Since effects are async, state updates from effects will happen later.
// The initial dispatch only updates the store synchronously.
// console.log('State after first dispatch (before effect completes):', store.getState());

// You would typically observe state changes via subscriptions or UI frameworks
store.subscribe(() => {
  console.log('Current state:', store.getState());
});