redux-immutable
redux-immutable is used to create an equivalent function of Redux combineReducers that works with Immutable.js state.
Top Related Projects
A JS library for predictable global state management
Simple, scalable state management.
Create the next immutable state by mutating the current one
The official, opinionated, batteries-included toolset for efficient Redux development
The Redux Framework
Quick Overview
Redux-immutable is a library that provides a seamless integration between Redux and Immutable.js. It allows developers to create a Redux store with an Immutable.js state, enabling efficient and predictable state management in React applications.
Pros
- Seamless integration of Redux with Immutable.js
- Improved performance due to Immutable.js's efficient data structures
- Enhanced state immutability, reducing bugs related to unintended state mutations
- Compatible with existing Redux ecosystem and tools
Cons
- Learning curve for developers unfamiliar with Immutable.js
- Potential increase in bundle size due to additional dependency
- May require refactoring of existing Redux code to work with Immutable.js structures
Code Examples
Creating a Redux store with an Immutable.js state:
import { createStore } from 'redux';
import { combineReducers } from 'redux-immutable';
import Immutable from 'immutable';
const initialState = Immutable.Map();
const rootReducer = combineReducers({
// Your reducers here
});
const store = createStore(rootReducer, initialState);
Accessing state in a connected component:
import { connect } from 'react-redux';
const mapStateToProps = (state) => ({
user: state.getIn(['user', 'data']),
isLoading: state.getIn(['user', 'isLoading']),
});
export default connect(mapStateToProps)(YourComponent);
Using Immutable.js in a reducer:
import { fromJS } from 'immutable';
const initialState = fromJS({
data: null,
isLoading: false,
error: null,
});
const userReducer = (state = initialState, action) => {
switch (action.type) {
case 'FETCH_USER_REQUEST':
return state.set('isLoading', true);
case 'FETCH_USER_SUCCESS':
return state.merge({
data: action.payload,
isLoading: false,
error: null,
});
default:
return state;
}
};
Getting Started
-
Install the required packages:
npm install redux-immutable immutable
-
Create your Redux store with an Immutable.js state:
import { createStore } from 'redux'; import { combineReducers } from 'redux-immutable'; import Immutable from 'immutable'; const initialState = Immutable.Map(); const rootReducer = combineReducers({ // Your reducers here }); const store = createStore(rootReducer, initialState);
-
Use Immutable.js methods to interact with the state in your reducers and components.
Competitor Comparisons
A JS library for predictable global state management
Pros of Redux
- Widely adopted and well-maintained by the Redux team
- Extensive ecosystem with many middleware and tools
- Flexible and can be used with any UI library or framework
Cons of Redux
- Requires more boilerplate code
- Learning curve can be steeper for beginners
- Performance can be an issue with large state trees
Code Comparison
Redux:
import { createStore } from 'redux'
const rootReducer = (state = {}, action) => {
// Reducer logic
}
const store = createStore(rootReducer)
Redux-Immutable:
import { createStore } from 'redux'
import { combineReducers } from 'redux-immutable'
import Immutable from 'immutable'
const rootReducer = combineReducers({
// Reducers
})
const store = createStore(rootReducer, Immutable.Map())
Key Differences
- Redux-Immutable focuses on using Immutable.js with Redux
- Redux-Immutable provides a custom
combineReducers
function - Redux-Immutable initializes the store with an Immutable.Map()
Use Cases
- Redux: General-purpose state management for React applications
- Redux-Immutable: When you want to leverage Immutable.js for performance and data integrity in Redux
Community and Support
- Redux: Large community, frequent updates, extensive documentation
- Redux-Immutable: Smaller community, less frequent updates, but still actively maintained
Simple, scalable state management.
Pros of MobX
- Simpler and more intuitive API, requiring less boilerplate code
- Automatic tracking of state changes and re-rendering of components
- Better performance for complex state updates due to fine-grained reactivity
Cons of MobX
- Less predictable state updates, as mutations are allowed
- Steeper learning curve for developers used to immutable state patterns
- Potential for overuse of observables, leading to unnecessary re-renders
Code Comparison
MobX:
import { makeObservable, observable, action } from "mobx";
class Store {
count = 0;
constructor() {
makeObservable(this, {
count: observable,
increment: action
});
}
increment() {
this.count++;
}
}
Redux-Immutable:
import { createStore } from 'redux';
import { fromJS } from 'immutable';
const initialState = fromJS({ count: 0 });
function reducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return state.update('count', count => count + 1);
default:
return state;
}
}
const store = createStore(reducer);
Redux-Immutable focuses on maintaining immutable state using Immutable.js, while MobX allows for direct mutations of observable state. MobX's approach is more concise but may lead to less predictable state management in larger applications. Redux-Immutable provides a more structured and predictable state flow, but requires more boilerplate code.
Create the next immutable state by mutating the current one
Pros of Immer
- Simpler API with a more intuitive approach to immutability
- Works with any data structure, not limited to Redux state
- Can be used in various contexts beyond Redux
Cons of Immer
- Slightly higher learning curve for developers new to the concept
- May introduce a small performance overhead in some cases
Code Comparison
Redux-immutable:
import { fromJS } from 'immutable';
import { combineReducers } from 'redux-immutable';
const initialState = fromJS({
todos: []
});
const rootReducer = combineReducers({
// reducers here
});
Immer:
import produce from 'immer';
const initialState = {
todos: []
};
const rootReducer = produce((draft, action) => {
// modify draft directly
});
Summary
Redux-immutable is specifically designed for use with Redux and Immutable.js, providing a way to create an immutable Redux store. It's well-suited for projects already using Immutable.js.
Immer, on the other hand, offers a more flexible approach to immutability that can be used in various contexts, including Redux. It allows developers to write simpler, more intuitive code while still maintaining immutability. Immer's API is generally easier to understand and use, especially for those new to immutable state management.
While Immer may have a slight performance cost in some scenarios, its simplicity and flexibility often outweigh this drawback for many developers and projects.
The official, opinionated, batteries-included toolset for efficient Redux development
Pros of Redux Toolkit
- Provides a comprehensive set of tools and utilities for Redux development
- Simplifies common Redux use cases with built-in functions like
createSlice
- Includes RTK Query for efficient API calls and data fetching
Cons of Redux Toolkit
- Larger bundle size due to additional features and dependencies
- Steeper learning curve for developers new to Redux concepts
Code Comparison
Redux Toolkit:
import { createSlice } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment: state => state + 1,
},
})
Redux Immutable:
import { fromJS } from 'immutable'
import { combineReducers } from 'redux-immutable'
const initialState = fromJS({
counter: 0,
})
const rootReducer = combineReducers({
counter: (state = initialState.get('counter'), action) => {
// reducer logic
},
})
Key Differences
- Redux Toolkit focuses on simplifying Redux development with built-in utilities
- Redux Immutable emphasizes using Immutable.js for state management
- Redux Toolkit has more active development and community support
- Redux Immutable is more lightweight but requires manual implementation of common Redux patterns
Use Cases
- Choose Redux Toolkit for comprehensive Redux development with modern best practices
- Opt for Redux Immutable when working with Immutable.js and needing a minimal Redux setup
The Redux Framework
Pros of Rematch
- Simplified Redux setup with less boilerplate code
- Built-in support for async actions and effects
- Easier to scale and maintain large applications
Cons of Rematch
- Learning curve for developers familiar with traditional Redux
- Less flexibility in some advanced Redux scenarios
- Potential performance overhead for very small applications
Code Comparison
Redux-immutable:
import { combineReducers } from 'redux-immutable';
import { fromJS } from 'immutable';
const initialState = fromJS({
counter: 0
});
const rootReducer = combineReducers({
counter: (state = initialState.get('counter'), action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
default:
return state;
}
}
});
Rematch:
import { init } from '@rematch/core';
const counter = {
state: 0,
reducers: {
increment(state) {
return state + 1;
}
}
};
const store = init({
models: { counter }
});
Redux-immutable focuses on combining reducers with Immutable.js data structures, while Rematch provides a more concise and intuitive API for defining models and actions. Rematch simplifies the Redux setup process and reduces boilerplate, but may require adapting to a different mental model for state management.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
redux-immutable
redux-immutable
is used to create an equivalent function of Redux combineReducers
that works with Immutable.js state.
When Redux createStore
reducer
is created using redux-immutable
then initialState
must be an instance of Immutable.Collection
.
Problem
When createStore
is invoked with initialState
that is an instance of Immutable.Collection
further invocation of reducer will produce an error:
The initialState argument passed to createStore has unexpected type of "Object". Expected argument to be an object with the following keys: "data"
This is because Redux combineReducers
treats state
object as a plain JavaScript object.
combineReducers
created using redux-immutable
uses Immutable.js API to iterate the state.
Usage
Create a store with initialState
set to an instance of Immutable.Collection
:
import {
combineReducers
} from 'redux-immutable';
import {
createStore
} from 'redux';
const initialState = Immutable.Map();
const rootReducer = combineReducers({});
const store = createStore(rootReducer, initialState);
By default, if state
is undefined
, rootReducer(state, action)
is called with state = Immutable.Map()
. A different default function can be provided as the second parameter to combineReducers(reducers, getDefaultState)
, for example:
const StateRecord = Immutable.Record({
foo: 'bar'
});
const rootReducer = combineReducers({foo: fooReducer}, StateRecord);
// rootReducer now has signature of rootReducer(state = StateRecord(), action)
// state now must always have 'foo' property with 'bar' as its default value
When using Immutable.Record
it is possible to delegate default values to child reducers:
const StateRecord = Immutable.Record({
foo: undefined
});
const rootReducer = combineReducers({foo: fooReducer}, StateRecord);
// state now must always have 'foo' property with its default value returned from fooReducer(undefined, action)
In general, getDefaultState
function must return an instance of Immutable.Record
or Immutable.Collection
that implements get
, set
and withMutations
methods. Such collections are List
, Map
and OrderedMap
.
Using with react-router-redux
v4 and under
react-router-redux
routeReducer
does not work with Immutable.js. You need to use a custom reducer:
import Immutable from 'immutable';
import {
LOCATION_CHANGE
} from 'react-router-redux';
const initialState = Immutable.fromJS({
locationBeforeTransitions: null
});
export default (state = initialState, action) => {
if (action.type === LOCATION_CHANGE) {
return state.set('locationBeforeTransitions', action.payload);
}
return state;
};
Pass a selector to access the payload state and convert it to a JavaScript object via the selectLocationState
option on syncHistoryWithStore
:
import {
browserHistory
} from 'react-router';
import {
syncHistoryWithStore
} from 'react-router-redux';
const history = syncHistoryWithStore(browserHistory, store, {
selectLocationState (state) {
return state.get('routing').toJS();
}
});
The 'routing'
path depends on the rootReducer
definition. This example assumes that routeReducer
is made available under routing
property of the rootReducer
.
Using with react-router-redux
v5
To make react-router-redux
v5 work with Immutable.js you only need to use a custom reducer:
import {
Map
} from 'immutable';
import {
LOCATION_CHANGE
} from 'react-router-redux';
const initialState = Map({
location: null,
action: null
});
export function routerReducer(state = initialState, {type, payload = {}} = {}) {
if (type === LOCATION_CHANGE) {
const location = payload.location || payload;
const action = payload.action;
return state
.set('location', location)
.set('action', action);
}
return state;
}
Top Related Projects
A JS library for predictable global state management
Simple, scalable state management.
Create the next immutable state by mutating the current one
The official, opinionated, batteries-included toolset for efficient Redux development
The Redux Framework
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot