How Does Compose Work for Combining Store Enhancers in Redux

Redux's compose utility chains store enhancers by applying function composition from right-to-left, creating a pipeline where each enhancer receives the output of the one to its right and returns an enhanced createStore function.

When building complex Redux applications, you often need to combine multiple store enhancers—such as middleware, DevTools integration, and custom wrappers—into a single enhancement pipeline. Understanding how compose works for combining store enhancers is essential for configuring your Redux store correctly. In the reduxjs/redux repository, the compose function in src/compose.ts provides the mechanism that makes this chaining possible.

What Is a Store Enhancer?

A store enhancer is a higher-order function that takes the original createStore implementation as its argument and returns a new, "enhanced" version of createStore. According to the source code in src/createStore.ts, enhancers allow you to extend the store's capabilities without modifying the core logic. Common examples include applyMiddleware (defined in src/applyMiddleware.ts), Redux DevTools integration, and custom persistence layers.

How Compose Works for Combining Store Enhancers

The compose function implements function composition from right-to-left, mathematically represented as compose(f, g, h) ≡ (...args) => f(g(h(...args))). When applied to store enhancers, this means each enhancer receives the output of the enhancer to its right, building a nested chain of enhanced createStore functions.

Right-to-Left Execution Order

When you call compose(enhancerA, enhancerB, enhancerC), the execution flows from right to left:

  1. enhancerC receives the base createStore and returns an enhanced version.
  2. enhancerB receives the result from enhancerC and wraps it further.
  3. enhancerA receives the result from enhancerB and applies the final enhancement.

The right-most enhancer runs first because it is closest to the original createStore call, while the left-most enhancer provides the final interface that createStore invokes.

Internal Implementation

The implementation in src/compose.ts handles three distinct cases:

  • No arguments: Returns the identity function (arg) => arg.
  • Single function: Returns that function unchanged.
  • Multiple functions: Uses Array.prototype.reduce to nest calls: funcs.reduce((a, b) => (...args) => a(b(...args))).

This reduction pattern creates the right-to-left composition chain where each function b is executed before function a.

Practical Example: Composing Multiple Enhancers

Here's a real-world configuration combining middleware and DevTools:

import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import { devToolsEnhancer } from 'redux-devtools-extension';
import rootReducer from './reducers';

const store = createStore(
  rootReducer,
  undefined,
  compose(
    applyMiddleware(thunk, logger),  // Runs second (left)
    devToolsEnhancer({ trace: true }) // Runs first (right)
  )
);

In this example from src/applyMiddleware.ts, applyMiddleware returns a store enhancer that adds middleware capabilities. The devToolsEnhancer runs first (right-most), wrapping createStore with DevTools instrumentation. Then applyMiddleware receives that enhanced creator and adds the middleware pipeline. The final composed function is passed to createStore in src/createStore.ts.

Summary

  • Store enhancers are higher-order functions that wrap createStore to add capabilities.
  • compose chains multiple enhancers using right-to-left function composition.
  • The right-most enhancer executes first, closest to the base createStore.
  • The implementation in src/compose.ts uses reduce to nest function calls: (...args) => a(b(...args)).
  • applyMiddleware in src/applyMiddleware.ts is the most common enhancer used with compose.

Frequently Asked Questions

What happens if I pass only one enhancer to compose?

If you pass a single function to compose, it returns that function unchanged. This is handled as a special case in src/compose.ts to avoid unnecessary wrapper overhead while maintaining consistent behavior.

Why does Redux compose work right-to-left instead of left-to-right?

Redux uses right-to-left composition to match mathematical function composition notation and to ensure that the enhancer closest to createStore (the right-most) executes first. This creates a natural nesting where each subsequent enhancer wraps the result of the previous one, maintaining the expected execution order for middleware and instrumentation.

Can I use compose without createStore?

Yes. While compose is primarily designed for combining store enhancers, it is a generic utility function exported from redux that can compose any functions where the output type of one matches the input type of the next. It works with any functions, not just Redux-specific ones.

How does applyMiddleware use compose internally?

The applyMiddleware function in src/applyMiddleware.ts uses compose to chain the middleware functions themselves. It composes the middleware chain so that each middleware receives the dispatch from the middleware to its right, creating the pipeline that processes actions before they reach the reducer.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →