Redux
  • Read Me
  • 소개
    • 동기
    • Core Concepts
    • 3가지 원칙
    • 기존 기술들
    • Learning Resources
    • 생태계
    • 예시
  • 기초
    • 액션
    • 리듀서
    • 스토어
    • 데이터 흐름
    • React와 함께 사용하기
    • 예시: Todo List
  • 심화
    • 비동기 액션
    • 비동기 흐름
    • 미들웨어
    • React Router와 함께 사용하기
    • 예시: Reddit API
    • Next Steps
  • 레시피
    • Configuring Your Store
    • Redux로 마이그레이션
    • 객체 확산 연산자 사용하기
    • 보일러플레이트 줄이기
    • Server Rendering
    • Writing Tests
    • Computing Derived Data
    • Implementing Undo History
    • Isolating Subapps
    • 리듀서 구조화하기
      • 사전에 요구되는 개념들
      • 기본 리듀서 구조
      • 리듀서 로직 분리하기
      • 리듀서 예제 리팩토링하기
      • combineReducers 사용하기
      • combineReducers 더 알아보기
      • 상태 정규화하기
      • 정규화된 데이터 업데이트하기
      • 리듀서 로직 재사용하기
      • 불변 업데이트 패턴
      • 상태 초기화하기
    • Using Immutable.JS with Redux
  • FAQ
    • General
    • Reducers
    • Organizing State
    • Store Setup
    • Actions
    • Immutable Data
    • Code Structure
    • Performance
    • Design Decisions
    • React Redux
    • Miscellaneous
  • 문제해결
  • 용어사전
  • API 레퍼런스
    • createStore
    • Store
    • combineReducers
    • applyMiddleware
    • bindActionCreators
    • compose
  • 변경 기록
  • 후원자
  • 피드백
Powered by GitBook
On this page
  • Table of Contents
  • Why should I use an immutable-focused library such as Immutable.JS?
  • Why should I choose Immutable.JS as an immutable library?
  • What are the issues with using Immutable.JS?
  • Once used, Immutable.JS will spread throughout your codebase
  • No Destructuring or Spread Operators
  • Not suitable for small values that change often
  • Difficult to Debug
  • Breaks object references, causing poor performance
  • Is Using Immutable.JS worth the effort?
  • What are some opinionated Best Practices for using Immutable.JS with Redux?
  • Never mix plain JavaScript objects with Immutable.JS
  • Make your entire Redux state tree an Immutable.JS object
  • Use Immutable.JS everywhere except your dumb components
  • Limit your use of toJS()
  • Your selectors should return Immutable.JS objects
  • Use Immutable.JS objects in your Smart Components
  • Never use toJS() in mapStateToProps
  • Never use Immutable.JS in your Dumb Components
  • Use a Higher Order Component to convert your Smart Component’s Immutable.JS props to your Dumb Component’s JavaScript props
  • Use the Immutable Object Formatter Chrome Extension to Aid Debugging
  1. 레시피

Using Immutable.JS with Redux

Previous상태 초기화하기NextFAQ

Last updated 6 years ago

Table of Contents

Why should I use an immutable-focused library such as Immutable.JS?

Immutable-focused libraries such as Immutable.JS have been designed to overcome the issues with immutability inherent within JavaScript, providing all the benefits of immutability with the performance your app requires.

Whether you choose to use such a library, or stick with plain JavaScript, depends on how comfortable you are with adding another dependency to your app, or how sure you are that you can avoid the pitfalls inherent within JavaScript’s approach to immutability.

Whichever option you choose, make sure you’re familiar with the concepts of . In particular, ensure you have a deep understanding of what JavaScript does when updating and copying values in order to guard against accidental mutations that will degrade your app’s performance, or break it altogether.

Further Information

Documentation

Articles

Why should I choose Immutable.JS as an immutable library?

Immutable.JS was designed to provide immutability in a performant manner in an effort to overcome the limitations of immutability with JavaScript. Its principle advantages include:

Guaranteed immutability

Data encapsulated in an Immutable.JS object is never mutated. A new copy is always returned. This contrasts with JavaScript, in which some operations do not mutate your data (e.g. some Array methods, including map, filter, concat, forEach, etc.), but some do (Array’s pop, push, splice, etc.).

Rich API

Immutable.JS provides a rich set of immutable objects to encapsulate your data (e.g. Maps, Lists, Sets, Records, etc.), and an extensive set of methods to manipulate it, including methods to sort, filter, and group the data, reverse it, flatten it, and create subsets.

Performance

Immutable.JS does a lot work behind the scenes to optimize performance. This is the key to its power, as using immutable data structures can involve a lot of expensive copying. In particular, immutably manipulating large, complex data sets, such as a nested Redux state tree, can generate many intermediate copies of objects, which consume memory and slow down performance as the browser’s garbage collector fights to clean things up.

You never see this, of course - the data you give to an Immutable.JS object is never mutated. Rather, it’s the intermediate data generated within Immutable.JS from a chained sequence of method calls that is free to be mutated. You therefore get all the benefits of immutable data structures with none (or very little) of the potential performance hits.

Further Information

Articles

Libraries

What are the issues with using Immutable.JS?

Although powerful, Immutable.JS needs to be used carefully, as it comes with issues of its own. Note, however, that all of these issues can be overcome quite easily with careful coding.

Difficult to interoperate with

JavaScript does not provide immutable data structures. As such, for Immutable.JS to provide its immutable guarantees, your data must be encapsulated within an Immutable.JS object (such as a Map or a List, etc.). Once it’s contained in this way, it’s hard for that data to then interoperate with other, plain JavaScript objects.

For example, you will no longer be able to reference an object’s properties through standard JavaScript dot or bracket notation. Instead, you must reference them via Immutable.JS’s get() or getIn() methods, which use an awkward syntax that accesses properties via an array of strings, each of which represents a property key.

For example, instead of myObj.prop1.prop2.prop3, you would use myImmutableMap.getIn([‘prop1’, ‘prop2’, ‘prop3’]).

This makes it awkward to interoperate not just with your own code, but also with other libraries, such as lodash or ramda, that expect plain JavaScript objects.

Note that Immutable.JS objects do have a toJS() method, which returns the data as a plain JavaScript data structure, but this method is extremely slow, and using it extensively will negate the performance benefits that Immutable.JS provides

Once used, Immutable.JS will spread throughout your codebase

Once you encapsulate your data with Immutable.JS, you have to use Immutable.JS’s get() or getIn() property accessors to access it.

This has the effect of spreading Immutable.JS across your entire codebase, including potentially your components, where you may prefer not to have such external dependencies. Your entire codebase must know what is, and what is not, an Immutable.JS object. It also makes removing Immutable.JS from your app difficult in the future, should you ever need to.

No Destructuring or Spread Operators

Because you must access your data via Immutable.JS’s own get() and getIn() methods, you can no longer use JavaScript’s destructuring operator (or the proposed Object spread operator), making your code more verbose.

Not suitable for small values that change often

Immutable.JS works best for collections of data, and the larger the better. It can be slow when your data comprises lots of small, simple JavaScript objects, with each comprising a few keys of primitive values.

Note, however, that this does not apply to the Redux state tree, which is (usually) represented as a large collection of data.

Difficult to Debug

Immutable.JS objects, such as Map, List, etc., can be difficult to debug, as inspecting such an object will reveal an entire nested hierarchy of Immutable.JS-specific properties that you don’t care about, while your actual data that you do care about is encapsulated several layers deep.

Breaks object references, causing poor performance

One of the key advantages of immutability is that it enables shallow equality checking, which dramatically improves performance.

If two different variables reference the same immutable object, then a simple equality check of the two variables is enough to determine that they are equal, and that the object they both reference is unchanged. The equality check never has to check the values of any of the object’s properties, as it is, of course, immutable.

However, shallow checking will not work if your data encapsulated within an Immutable.JS object is itself an object. This is because Immutable.JS’s toJS() method, which returns the data contained within an Immutable.JS object as a JavaScript value, will create a new object every time it’s called, and so break the reference with the encapsulated data.

Accordingly, calling toJS() twice, for example, and assigning the result to two different variables will cause an equality check on those two variables to fail, even though the object values themselves haven’t changed.

This is a particular issue if you use toJS() in a wrapped component’s mapStateToProps function, as React-Redux shallowly compares each value in the returned props object. For example, the value referenced by the todos prop returned from mapStateToProps below will always be a different object, and so will fail a shallow equality check.

// AVOID .toJS() in mapStateToProps
function mapStateToProps(state) {
  return {
    todos: state.get('todos').toJS() // Always a new object
  }
}

When the shallow check fails, React-Redux will cause the component to re-render. Using toJS() in mapStateToProps in this way, therefore, will always cause the component to re-render, even if the value never changes, impacting heavily on performance.

Further Information

Articles

Chrome Extension

Is Using Immutable.JS worth the effort?

Frequently, yes. There are various tradeoffs and opinions to consider, but there are many good reasons to use Immutable.JS. Do not underestimate the difficulty of trying to track down a property of your state tree that has been inadvertently mutated.

Components will both re-render when they shouldn’t, and refuse to render when they should, and tracking down the bug causing the rendering issue is hard, as the component rendering incorrectly is not necessarily the one whose properties are being accidentally mutated.

This problem is caused predominantly by returning a mutated state object from a Redux reducer. With Immutable.JS, this problem simply does not exist, thereby removing a whole class of bugs from your app.

This, together with its performance and rich API for data manipulation, is why Immutable.JS is worth the effort.

Further Information

Documentation

What are some opinionated Best Practices for using Immutable.JS with Redux?

Immutable.JS can provide significant reliability and performance improvements to your app, but it must be used correctly. If you choose to use Immutable.JS (and remember, you are not required to, and there are other immutable libraries you can use), follow these opinionated best practices, and you’ll be able to get the most out of it, without tripping up on any of the issues it can potentially cause.

Never mix plain JavaScript objects with Immutable.JS

Never let a plain JavaScript object contain Immutable.JS properties. Equally, never let an Immutable.JS object contain a plain JavaScript object.

Further Information

Articles

Make your entire Redux state tree an Immutable.JS object

For a Redux app, your entire state tree should be an Immutable.JS object, with no plain JavaScript objects used at all.

  • Create the tree using Immutable.JS’s fromJS() function.

  • When adding JavaScript objects to an Immutable.JS Map or List using Immutable.JS’s update, merge or set methods, ensure that the object being added is first converted to an Immutable object using fromJS().

Example

// avoid
const newObj = { key: value }
const newState = state.setIn(['prop1'], newObj)
// newObj has been added as a plain JavaScript object, NOT as an Immutable.JS Map

// recommended
const newObj = { key: value }
const newState = state.setIn(['prop1'], fromJS(newObj))
// newObj is now an Immutable.JS Map

Further Information

Articles

Libraries

Use Immutable.JS everywhere except your dumb components

Using Immutable.JS everywhere keeps your code performant. Use it in your smart components, your selectors, your sagas or thunks, action creators, and especially your reducers.

Do not, however, use Immutable.JS in your dumb components.

Further Information

Articles

Limit your use of toJS()

toJS() is an expensive function and negates the purpose of using Immutable.JS. Avoid its use.

Further Information

Discussions

Your selectors should return Immutable.JS objects

Always.

Use Immutable.JS objects in your Smart Components

Smart components that access the store via React Redux’s connect function must use the Immutable.JS values returned by your selectors. Make sure you avoid the potential issues this can cause with unnecessary component re-rendering. Memoize your selectors using a library such as reselect if necessary.

Further Information

Documentation

Articles

Libraries

Never use toJS() in mapStateToProps

Converting an Immutable.JS object to a JavaScript object using toJS() will return a new object every time. If you do this in mapSateToProps, you will cause the component to believe that the object has changed every time the state tree changes, and so trigger an unnecessary re-render.

Further Information

Documentation

Never use Immutable.JS in your Dumb Components

Your dumb components should be pure; that is, they should produce the same output given the same input, and have no external dependencies. If you pass such a component an Immutable.JS object as a prop, you make it dependent upon Immutable.JS to extract the prop’s value and otherwise manipulate it.

Such a dependency renders the component impure, makes testing the component more difficult, and makes reusing and refactoring the component unnecessarily difficult.

Further Information

Articles

Use a Higher Order Component to convert your Smart Component’s Immutable.JS props to your Dumb Component’s JavaScript props

Something needs to map the Immutable.JS props in your Smart Component to the pure JavaScript props used in your Dumb Component. That something is a Higher Order Component (HOC) that simply takes the Immutable.JS props from your Smart Component, and converts them using toJS() to plain JavaScript props, which are then passed to your Dumb Component.

Here is an example of such a HOC:

import React from 'react'
import { Iterable } from 'immutable'

export const toJS = WrappedComponent => wrappedComponentProps => {
  const KEY = 0
  const VALUE = 1

  const propsJS = Object.entries(
    wrappedComponentProps
  ).reduce((newProps, wrappedComponentProp) => {
    newProps[wrappedComponentProp[KEY]] = Iterable.isIterable(
      wrappedComponentProp[VALUE]
    )
      ? wrappedComponentProp[VALUE].toJS()
      : wrappedComponentProp[VALUE]
    return newProps
  }, {})

  return <WrappedComponent {...propsJS} />
}

And this is how you would use it in your Smart Component:

import { connect } from 'react-redux'

import { toJS } from './to-js'
import DumbComponent from './dumb.component'

const mapStateToProps = state => {
  return {
    // obj is an Immutable object in Smart Component, but it’s converted to a plain
    // JavaScript object by toJS, and so passed to DumbComponent as a pure JavaScript
    // object. Because it’s still an Immutable.JS object here in mapStateToProps, though,
    // there is no issue with errant re-renderings.
    obj: getImmutableObjectFromStateTree(state)
  }
}
export default connect(mapStateToProps)(toJS(DumbComponent))

By converting Immutable.JS objects to plain JavaScript values within a HOC, we achieve Dumb Component portability, but without the performance hits of using toJS() in the Smart Component.

Note: if your app requires high performance, you may need to avoid toJS() altogether, and so will have to use Immutable.JS in your dumb components. However, for most apps this will not be the case, and the benefits of keeping Immutable.JS out of your dumb components (maintainability, portability and easier testing) will far outweigh any perceived performance improvements of keeping it in.

In addition, using toJS in a Higher Order Component should not cause much, if any, performance degradation, as the component will only be called when the connected component’s props change. As with any performance issue, conduct performance checks first before deciding what to optimise.

Further Information

Documentation

Articles

Discussions

Gists

Use the Immutable Object Formatter Chrome Extension to Aid Debugging

Further Information

Chrome Extension

Immutable.JS avoids this by under the surface, minimizing the need to copy data. It also enables complex chains of operations to be carried out without creating unnecessary (and costly) cloned intermediate data that will quickly be thrown away.

This issue can be avoided by , as outlined in the below.

To resolve this issue, use a browser extension such as the , which surfaces your data in Chrome Dev Tools, and hides Immutable.JS’s properties when inspecting your data.

This can be prevented by using toJS() in a Higher Order Component, as discussed in the below.

Use an Immutable.JS-aware version of the combineReducers function, such as the one in , as Redux itself expects the state tree to be a plain JavaScript object.

Install the , and inspect your Immutable.JS data without seeing the noise of Immutable.JS's own object properties.

cleverly sharing data structures
Immutable.js, persistent data structures and structural sharing
PDF: JavaScript Immutability - Don’t go changing
Immutable.js, persistent data structures and structural sharing
Immutable.js Object Formatter
Immutable.js, persistent data structures and structural sharing
Immutable Data Structures and JavaScript
React.js pure render performance anti-pattern
Building Efficient UI with React and Redux
Immutable Object Formatter
Troubleshooting: Nothing happens when I dispatch an action
Immutable Data Structures and JavaScript
redux-immutable
Immutable Data Structures and JavaScript
redux-immutable
Immutable Data Structures and JavaScript
Smart and Dumb Components in React
Lee Byron on Twitter: “Perf tip for #immutablejs…”
Recipes: Computing Derived Data
FAQ: Immutable Data
Reselect Documentation: How do I use Reselect with Immutable.js?
Redux Patterns and Anti-Patterns
Reselect: Selector library for Redux
FAQ: Immutable Data
Immutable Data Structures and JavaScript
Smart and Dumb Components in React
Tips For a Better Redux Architecture: Lessons for Enterprise Scale
React: Higher-Order Components
React Higher Order Components in depth
Reddit: acemarke and cpsubrian comments on Dan Abramov: Redux is not an architecture or design pattern, it is just a library.
cpsubrian: React decorators for redux/react-router/immutable ‘smart’ components
Immutable Object Formatter
Immutable Object Formatter
immutability, side effects and mutation
Recipes: immutability, side effects and mutation
Introduction to Immutable.js and Functional Programming Concepts
Pros and Cons of using immutability with React.js
Why should I use an immutable-focused library such as Immutable.JS?
Why should I choose Immutable.JS as an immutable library?
What are the issues with using Immutable.JS?
Is Immutable.JS worth the effort?
What are some opinionated Best Practices for using Immutable.JS with Redux?
uncoupling your application logic from your data structures
best practices section
Best Practices section