diff --git a/docs/FAQ.md b/docs/FAQ.md index 9535af958b9..20a9916416c 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -34,14 +34,8 @@ hide_title: true - **Immutable Data** - [What are the benefits of immutability?](faq/ImmutableData.md#what-are-the-benefits-of-immutability) - [Why is immutability required by Redux?](faq/ImmutableData.md#why-is-immutability-required-by-redux) - - [What approaches are there for handling data immutability? Do I have to use Immutable.JS?](faq/ImmutableData.md#what-approaches-are-there-for-handling-data-immutability-do-i-have-to-use-immutable-js) + - [What approaches are there for handling data immutability? Do I have to use Immer?](faq/ImmutableData.md#what-approaches-are-there-for-handling-data-immutability-do-i-have-to-use-immer) - [What are the issues with using JavaScript for immutable operations?](faq/ImmutableData.md#what-are-the-issues-with-using-plain-javascript-for-immutable-operations) -- **Using Immutable.JS with Redux** - - [Why should I use an immutable-focused library such as Immutable.JS?](recipes/UsingImmutableJS.md#why-should-i-use-an-immutable-focused-library-such-as-immutable-js) - - [Why should I choose Immutable.JS as an immutable library?](recipes/UsingImmutableJS.md#why-should-i-choose-immutable-js-as-an-immutable-library) - - [What are the issues with using Immutable.JS?](recipes/UsingImmutableJS.md#what-are-the-issues-with-using-immutable-js) - - [Is Immutable.JS worth the effort?](recipes/UsingImmutableJS.md#is-using-immutable-js-worth-the-effort) - - [What are some opinionated Best Practices for using Immutable.JS with Redux?](recipes/UsingImmutableJS.md#what-are-some-opinionated-best-practices-for-using-immutable-js-with-redux) - **Code Structure** - [What should my file structure look like? How should I group my action creators and reducers in my project? Where should my selectors go?](faq/CodeStructure.md#what-should-my-file-structure-look-like-how-should-i-group-my-action-creators-and-reducers-in-my-project-where-should-my-selectors-go) - [How should I split my logic between reducers and action creators? Where should my “business logic” go?](faq/CodeStructure.md#how-should-i-split-my-logic-between-reducers-and-action-creators-where-should-my-business-logic-go) diff --git a/docs/api/createStore.md b/docs/api/createStore.md index 965bf32d3de..8b22c38b671 100644 --- a/docs/api/createStore.md +++ b/docs/api/createStore.md @@ -50,7 +50,7 @@ console.log(store.getState()) - Don't create more than one store in an application! Instead, use [`combineReducers`](combineReducers.md) to create a single root reducer out of many. -- It is up to you to choose the state format. You can use plain objects or something like [Immutable](https://facebook.github.io/immutable-js/). If you're not sure, start with plain objects. +- Redux state is normally plain JS objects and arrays. - If your state is a plain object, make sure you never mutate it! For example, instead of returning something like `Object.assign(state, newData)` from your reducers, return `Object.assign({}, state, newData)`. This way you don't override the previous `state`. You can also write `return { ...state, ...newData }` if you enable the [object spread operator proposal](../recipes/UsingObjectSpreadOperator.md). diff --git a/docs/faq/ImmutableData.md b/docs/faq/ImmutableData.md index e5d440a791f..317255da7b7 100644 --- a/docs/faq/ImmutableData.md +++ b/docs/faq/ImmutableData.md @@ -23,7 +23,7 @@ hide_title: true - [How does immutability enable a shallow check to detect object mutations?](#how-does-immutability-enable-a-shallow-check-to-detect-object-mutations) - [How can immutability in your reducers cause components to render unnecessarily?](#how-can-immutability-in-your-reducers-cause-components-to-render-unnecessarily) - [How can immutability in mapStateToProps cause components to render unnecessarily?](#how-can-immutability-in-mapstatetoprops-cause-components-to-render-unnecessarily) -- [What approaches are there for handling data immutability? Do I have to use Immutable.JS?](#what-approaches-are-there-for-handling-data-immutability-do-i-have-to-use-immutable-js) +- [What approaches are there for handling data immutability? Do I have to use Immer?](#what-approaches-are-there-for-handling-data-immutability-do-i-have-to-use-immer) - [What are the issues with using JavaScript for immutable operations?](#what-are-the-issues-with-using-plain-javascript-for-immutable-operations) ## What are the benefits of immutability? @@ -36,9 +36,8 @@ In particular, immutability in the context of a Web app enables sophisticated ch **Articles** -- [Introduction to Immutable.js and Functional Programming Concepts](https://auth0.com/blog/intro-to-immutable-js/) +- [Introduction to Immer](https://immerjs.github.io/immer/) - [JavaScript Immutability presentation (PDF - see slide 12 for benefits)](https://www.jfokus.se/jfokus16/preso/JavaScript-Immutability--Dont-Go-Changing.pdf) -- [Immutable.js - Immutable Collections for JavaScript](https://facebook.github.io/immutable-js/#the-case-for-immutability) - [React: Optimizing Performance](https://facebook.github.io/react/docs/optimizing-performance.html) - [JavaScript Application Architecture On The Road To 2015](https://medium.com/google-developers/javascript-application-architecture-on-the-road-to-2015-d8125811101b#.djje0rfys) @@ -431,18 +430,18 @@ Note that, conversely, if the values in your props object refer to mutable objec - [Building Efficient UI with React and Redux](https://www.toptal.com/react/react-redux-and-immutablejs) - [ImmutableJS: worth the price?](https://medium.com/@AlexFaunt/immutablejs-worth-the-price-66391b8742d4#.a3alci2g8) -## What approaches are there for handling data immutability? Do I have to use Immutable.JS? +## What approaches are there for handling data immutability? Do I have to use Immer? -You do not need to use Immutable.JS with Redux. Plain JavaScript, if written correctly, is perfectly capable of providing immutability without having to use an immutable-focused library. +You do not need to use Immer with Redux. Plain JavaScript, if written correctly, is perfectly capable of providing immutability without having to use an immutable-focused library. -However, guaranteeing immutability with JavaScript is difficult, and it can be easy to mutate an object accidentally, causing bugs in your app that are extremely difficult to locate. For this reason, using an immutable update utility library such as Immutable.JS can significantly improve the reliability of your app, and make your app’s development much easier. +However, guaranteeing immutability with JavaScript is difficult, and it can be easy to mutate an object accidentally, causing bugs in your app that are extremely difficult to locate. For this reason, using an immutable update utility library such as Immer can significantly improve the reliability of your app, and make your app’s development much easier. #### Further Information **Discussions** - [#1185: Question: Should I use immutable data structures?](https://github.com/reduxjs/redux/issues/1422) -- [Introduction to Immutable.js and Functional Programming Concepts](https://auth0.com/blog/intro-to-immutable-js/) +- [Introduction to Immer](https://immerjs.github.io/immer/) ## What are the issues with using plain JavaScript for immutable operations? @@ -464,9 +463,7 @@ Operating on JavaScript objects and arrays in an immutable way can be slow, part Remember, to change an immutable object, you must mutate a _copy_ of it, and copying large objects can be slow as every property must be copied. -In contrast, immutable libraries such as Immutable.JS can employ sophisticated optimization techniques such as [structural sharing](https://www.slideshare.net/mohitthatte/a-deep-dive-into-clojures-data-structures-euroclojure-2015) , which effectively returns a new object that reuses much of the existing object being copied from. - -For copying very large objects, [plain JavaScript can be over 100 times slower](https://medium.com/@dtinth/immutable-js-persistent-data-structures-and-structural-sharing-6d163fbd73d2#.z1g1ofrsi) than an optimized immutable library. +In contrast, immutable libraries such as Immer can employ structural sharing, which effectively returns a new object that reuses much of the existing object being copied from. #### Further Information @@ -476,9 +473,7 @@ For copying very large objects, [plain JavaScript can be over 100 times slower]( **Articles** -- [Immutable.js, persistent data structures and structural sharing](https://medium.com/@dtinth/immutable-js-persistent-data-structures-and-structural-sharing-6d163fbd73d2#.a2jimoiaf) - [A deep dive into Clojure’s data structures](https://www.slideshare.net/mohitthatte/a-deep-dive-into-clojures-data-structures-euroclojure-2015) -- [Introduction to Immutable.js and Functional Programming Concepts](https://auth0.com/blog/intro-to-immutable-js/) - [JavaScript and Immutability](https://t4d.io/javascript-and-immutability/) - [Immutable Javascript using ES6 and beyond](https://wecodetheweb.com/2016/02/12/immutable-javascript-using-es6-and-beyond/) - [Pros and Cons of using immutability with React.js - React Kung Fu](https://reactkungfu.com/2015/08/pros-and-cons-of-using-immutability-with-react-js/) diff --git a/docs/faq/ReactRedux.md b/docs/faq/ReactRedux.md index e9eac838bd0..06b05e5eae4 100644 --- a/docs/faq/ReactRedux.md +++ b/docs/faq/ReactRedux.md @@ -54,7 +54,7 @@ Accidentally mutating or modifying your state directly is by far the most common It's important to remember that whenever you update a nested value, you must also return new copies of anything above it in your state tree. If you have `state.a.b.c.d`, and you want to make an update to `d`, you would also need to return new copies of `c`, `b`, `a`, and `state`. This [state tree mutation diagram](http://arqex.com/wp-content/uploads/2015/02/trees.png) demonstrates how a change deep in a tree requires changes all the way up. -Note that “updating data immutably” does _not_ mean that you must use [Immutable.js](https://facebook.github.io/immutable-js/), although that is certainly an option. You can do immutable updates to plain JS objects and arrays using several different approaches: +Note that “updating data immutably” does _not_ mean that you must use [Immer](https://github.com/immerjs/immer), although that is certainly an option. You can do immutable updates to plain JS objects and arrays using several different approaches: - Copying objects using functions like `Object.assign()` or `_.extend()`, and array functions such as `slice()` and `concat()` - The array spread operator in ES6, and the similar object spread operator that is proposed for a future version of JavaScript diff --git a/docs/introduction/Ecosystem.md b/docs/introduction/Ecosystem.md index c7698eaed0f..f051e7a84e7 100644 --- a/docs/introduction/Ecosystem.md +++ b/docs/introduction/Ecosystem.md @@ -303,45 +303,7 @@ store.dispatch({ ## Immutable Data -#### Data Structures - -**[facebook/immutable-js](https://github.com/facebook/immutable-js)**
-Immutable persistent data collections for Javascript - -```js -const map1 = Map({ a: 1, b: 2, c: 3 }) -const map2 = map1.set('b', 50) -map1.get('b') // 2 -map2.get('b') // 50 -``` - -**[rtfeldman/seamless-immutable](https://github.com/rtfeldman/seamless-immutable)**
-Frozen immutable arrays/objects, backwards-compatible with JS - -```js -const array = Immutable(['totally', 'immutable', { a: 42 }]) -array[0] = 'edited' // does nothing -``` - -**[planttheidea/crio](https://github.com/planttheidea/crio)**
-Immutable JS objects with a natural API - -```js -const foo = crio(['foo']) -const fooBar = foo.push('bar') // new array: ['foo', 'bar'] -``` - -**[aearly/icepick](https://github.com/aearly/icepick)**
-Utilities for treating frozen JS objects as persistent immutable collections. - -```js -const newObj = icepick.assocIn({ c: { d: 'bar' } }, ['c', 'd'], 'baz') -const obj3 = icepicke.merge(obj1, obj2) -``` - -#### Immutable Update Utilities - -**[mweststrate/immer](https://github.com/mweststrate/immer)**
+**[ImmerJS/immer](https://github.com/immerjs/immer)**
Immutable updates with normal mutative code, using Proxies ```js @@ -351,50 +313,6 @@ const nextState = produce(baseState, draftState => { }) ``` -**[kolodny/immutability-helper](https://github.com/kolodny/immutability-helper)**
-A drop-in replacement for react-addons-update - -```js -const newData = update(myData, { - x: { y: { z: { $set: 7 } } }, - a: { b: { $push: [9] } } -}) -``` - -**[mariocasciaro/object-path-immutable](https://github.com/mariocasciaro/object-path-immutable)**
-Simpler alternative to immutability-helpers and Immutable.js - -```js -const newObj = immutable(obj).set('a.b', 'f').del(['a', 'c', 0]).value() -``` - -**[debitoor/dot-prop-immutable](https://github.com/debitoor/dot-prop-immutable)**
-Immutable version of the dot-prop lib, with some extensions - -```js -const newState = dotProp.set(state, `todos.${index}.complete`, true) -const endOfArray = dotProp.get(obj, 'foo.$end') -``` - -#### Immutable/Redux Interop - -**[gajus/redux-immutable](https://github.com/gajus/redux-immutable)**
-combineReducers equivalent that works with Immutable.js Maps - -```js -const initialState = Immutable.Map() -const rootReducer = combineReducers({}) -const store = createStore(rootReducer, initialState) -``` - -**[eadmundo/redux-seamless-immutable](https://github.com/eadmundo/redux-seamless-immutable)**
-combineReducers equivalent that works with seamless-immutable values - -```js -import { combineReducers } from 'redux-seamless-immutable'; -const rootReducer = combineReducers({ userReducer, posts -``` - ## Side Effects #### Widely Used diff --git a/docs/recipes/README.md b/docs/recipes/README.md index 281f8e8a90a..329015a94a9 100644 --- a/docs/recipes/README.md +++ b/docs/recipes/README.md @@ -18,7 +18,6 @@ These are some use cases and code snippets to get you started with Redux in a re - [Computing Derived Data](ComputingDerivedData.md) - [Implementing Undo History](ImplementingUndoHistory.md) - [Isolating Redux Sub-Apps](IsolatingSubapps.md) -- [Using Immutable.JS with Redux](UsingImmutableJS.md) - [Code Splitting](CodeSplitting.md) - [Troubleshooting](Troubleshooting.md) - [Structuring Reducers](structuring-reducers/StructuringReducers.md) diff --git a/docs/recipes/Troubleshooting.md b/docs/recipes/Troubleshooting.md index 3607f5b4607..bd57e5a5777 100644 --- a/docs/recipes/Troubleshooting.md +++ b/docs/recipes/Troubleshooting.md @@ -17,7 +17,7 @@ Sometimes, you are trying to dispatch an action, but your view does not update. It is tempting to modify the `state` or `action` passed to you by Redux. Don't do this! -Redux assumes that you never mutate the objects it gives to you in the reducer. **Every single time, you must return the new state object.** Even if you don't use a library like [Immutable](https://facebook.github.io/immutable-js/), you need to completely avoid mutation. +Redux assumes that you never mutate the objects it gives to you in the reducer. **Every single time, you must return the new state object.** Even if you don't use a library like [Immer](https://github.com/immerjs/immer), you need to completely avoid mutation. Immutability is what lets [react-redux](https://github.com/gaearon/react-redux) efficiently subscribe to fine-grained updates of your state. It also enables great developer experience features such as time travel with [redux-devtools](https://github.com/reduxjs/redux-devtools). diff --git a/docs/recipes/UsingImmutableJS.md b/docs/recipes/UsingImmutableJS.md deleted file mode 100644 index 0bc3b7abeda..00000000000 --- a/docs/recipes/UsingImmutableJS.md +++ /dev/null @@ -1,366 +0,0 @@ ---- -id: using-immutablejs-with-redux -title: Using Immutable.JS with Redux -hide_title: true ---- - -# Using Immutable.JS with Redux - -## Table of Contents - -- [Why should I use an immutable-focused library such as Immutable.JS?](#why-should-i-use-an-immutable-focused-library-such-as-immutablejs) -- [Why should I choose Immutable.JS as an immutable library?](#why-should-i-choose-immutablejs-as-an-immutable-library) -- [What are the issues with using Immutable.JS?](#what-are-the-issues-with-using-immutablejs) -- [Is Immutable.JS worth the effort?](#is-using-immutablejs-worth-the-effort) -- [What are some opinionated Best Practices for using Immutable.JS with Redux?](#what-are-some-opinionated-best-practices-for-using-immutablejs-with-redux) - -## 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 [immutability, side effects and mutation](./structuring-reducers/PrerequisiteConcepts.md#note-on-immutability-side-effects-and-mutation). 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** - -- [Recipes: immutability, side effects and mutation](./structuring-reducers/PrerequisiteConcepts.md#note-on-immutability-side-effects-and-mutation) - -**Articles** - -- [Introduction to Immutable.js and Functional Programming Concepts](https://auth0.com/blog/intro-to-immutable-js/) -- [Pros and Cons of using immutability with React.js](https://reactkungfu.com/2015/08/pros-and-cons-of-using-immutability-with-react-js/) - -## 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 of 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. - -Immutable.JS avoids this by [cleverly sharing data structures](https://medium.com/@dtinth/immutable-js-persistent-data-structures-and-structural-sharing-6d163fbd73d2#.z1g1ofrsi) 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. - -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** - -- [Immutable.js, persistent data structures and structural sharing](https://medium.com/@dtinth/immutable-js-persistent-data-structures-and-structural-sharing-6d163fbd73d2#.6nwctunlc) -- [PDF: JavaScript Immutability - Don’t go changing](https://www.jfokus.se/jfokus16/preso/JavaScript-Immutability--Dont-Go-Changing.pdf) - -**Libraries** - -- [Immutable.js](https://facebook.github.io/immutable-js/) - -## 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. - -This issue can be avoided by [uncoupling your application logic from your data structures](https://medium.com/@dtinth/immutable-js-persistent-data-structures-and-structural-sharing-6d163fbd73d2#.z1g1ofrsi), as outlined in the [best practices section](#what-are-some-opinionated-best-practices-for-using-immutablejs-with-redux) below. - -### 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. - -To resolve this issue, use a browser extension such as the [Immutable.js Object Formatter](https://chrome.google.com/webstore/detail/immutablejs-object-format/hgldghadipiblonfkkicmgcbbijnpeog), which surfaces your data in Chrome Dev Tools, and hides Immutable.JS’s properties when inspecting your data. - -### 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. - -```js -// 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. - -This can be prevented by using `toJS()` in a Higher Order Component, as discussed in the [Best Practices section](#what-are-some-opinionated-best-practices-for-using-immutablejs-with-redux) below. - -#### Further Information - -**Articles** - -- [Immutable.js, persistent data structures and structural sharing](https://medium.com/@dtinth/immutable-js-persistent-data-structures-and-structural-sharing-6d163fbd73d2#.hzgz7ghbe) -- [Immutable Data Structures and JavaScript](https://jlongster.com/Using-Immutable-Data-Structures-in-JavaScript) -- [React.js pure render performance anti-pattern](https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f#.9ucv6hwk4) -- [Building Efficient UI with React and Redux](https://www.toptal.com/react/react-redux-and-immutablejs) - -**Chrome Extension** - -- [Immutable Object Formatter](https://chrome.google.com/webstore/detail/immutablejs-object-format/hgldghadipiblonfkkicmgcbbijnpeog) - -## 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** - -- [Troubleshooting: Nothing happens when I dispatch an action](./Troubleshooting.md#nothing-happens-when-i-dispatch-an-action) - -## 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** - -- [Immutable Data Structures and JavaScript](https://jlongster.com/Using-Immutable-Data-Structures-in-JavaScript) - -### 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. - -- Use an Immutable.JS-aware version of the `combineReducers` function, such as the one in [redux-immutable](https://www.npmjs.com/package/redux-immutable), as Redux itself expects the state tree to be a plain JavaScript object. - -- 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** - -```js -// 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** - -- [Immutable Data Structures and JavaScript](https://jlongster.com/Using-Immutable-Data-Structures-in-JavaScript) - -**Libraries** - -- [redux-immutable](https://www.npmjs.com/package/redux-immutable) - -### 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** - -- [Immutable Data Structures and JavaScript](https://jlongster.com/Using-Immutable-Data-Structures-in-JavaScript) -- [Smart and Dumb Components in React](https://jaketrent.com/post/smart-dumb-components-react/) - -### Limit your use of `toJS()` - -`toJS()` is an expensive function and negates the purpose of using Immutable.JS. Avoid its use. - -#### Further Information - -** Discussions** - -- [Lee Byron on Twitter: “Perf tip for #immutablejs…”](https://twitter.com/leeb/status/746733697093668864) - -### Your selectors should return Immutable.JS objects - -Always. This practice has several advantages: - -- It avoids unnecessary rerenders caused by calling `.toJS()` in selectors (since `.toJS()` will always return a new object). - - It is possible to memoize selectors where you call `.toJS()`, but it’s redundant when just returning Immutable.js objects without memoizing will suffice. -- It establishes a consistent interface for selectors; you won’t have to keep track of whether an Immutable.js object or plain JavaScript object will be returned. - -### 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** - -- [Recipes: Computing Derived Data](./ComputingDerivedData.md) -- [FAQ: Immutable Data](../faq/ImmutableData.md#immutability-issues-with-react-redux) -- [Reselect Documentation: How do I use Reselect with Immutable.js?](https://github.com/reduxjs/reselect/#q-how-do-i-use-reselect-with-immutablejs) - -**Articles** - -- [Redux Patterns and Anti-Patterns](https://tech.affirm.com/redux-patterns-and-anti-patterns-7d80ef3d53bc#.451p9ycfy) - -**Libraries** - -- [Reselect: Selector library for Redux](https://github.com/reduxjs/reselect) - -### 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 `mapStateToProps`, 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** - -- [FAQ: Immutable Data](../faq/ImmutableData.md#how-can-immutability-in-mapstatetoprops-cause-components-to-render-unnecessarily) - -### 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** - -- [Immutable Data Structures and JavaScript](https://jlongster.com/Using-Immutable-Data-Structures-in-JavaScript) -- [Smart and Dumb Components in React](https://jaketrent.com/post/smart-dumb-components-react/) -- [Tips For a Better Redux Architecture: Lessons for Enterprise Scale](https://hashnode.com/post/tips-for-a-better-redux-architecture-lessons-for-enterprise-scale-civrlqhuy0keqc6539boivk2f) - -### 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. - -An example of such a HOC follows. A similar HOC is available as an NPM package for your convenience: [with-immutable-props-to-js](https://www.npmjs.com/package/with-immutable-props-to-js). - -```js -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 -} -``` - -And this is how you would use it in your Smart Component: - -```js -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 optimize._ - -#### Further Information - -**Documentation** - -- [React: Higher-Order Components](https://facebook.github.io/react/docs/higher-order-components.html) - -**Articles** - -- [React Higher Order Components in depth](https://medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e#.dw2qd1o1g) - -**Discussions** - -- [Reddit: acemarke and cpsubrian comments on Dan Abramov: Redux is not an architecture or design pattern, it is just a library.](https://www.reddit.com/r/javascript/comments/4rcqpx/dan_abramov_redux_is_not_an_architecture_or/d5rw0p9/?context=3) - -**Gists** - -- [cpsubrian: React decorators for redux/react-router/immutable ‘smart’ components](https://gist.github.com/cpsubrian/79e97b6116ab68bd189eb4917203242c#file-tojs-js) - -### Use the Immutable Object Formatter Chrome Extension to Aid Debugging - -Install the [Immutable Object Formatter](https://chrome.google.com/webstore/detail/immutablejs-object-format/hgldghadipiblonfkkicmgcbbijnpeog) , and inspect your Immutable.JS data without seeing the noise of Immutable.JS's own object properties. - -#### Further Information - -**Chrome Extension** - -- [Immutable Object Formatter](https://chrome.google.com/webstore/detail/immutablejs-object-format/hgldghadipiblonfkkicmgcbbijnpeog) diff --git a/docs/recipes/structuring-reducers/BeyondCombineReducers.md b/docs/recipes/structuring-reducers/BeyondCombineReducers.md index 832e2a407ad..641adcc7db4 100644 --- a/docs/recipes/structuring-reducers/BeyondCombineReducers.md +++ b/docs/recipes/structuring-reducers/BeyondCombineReducers.md @@ -11,10 +11,6 @@ The `combineReducers` utility included with Redux is very useful, but is deliber The common question, then, is "How can I use `combineReducers` to handle these other use cases?". The answer to that is simply: "you don't - you probably need to use something else". **Once you go past the core use case for `combineReducers`, it's time to use more "custom" reducer logic**, whether it be specific logic for a one-off use case, or a reusable function that could be widely shared. Here's some suggestions for dealing with a couple of these typical use cases, but feel free to come up with your own approaches. -## Using slice reducers with Immutable.js objects - -Since `combineReducers` currently only works with plain Javascript objects, an application that uses an Immutable.js Map object for the top of its state tree could not use `combineReducers` to manage that Map. Since many developers do use Immutable.js, there are a number of published utilities that provide equivalent functionality, such as [redux-immutable](https://github.com/gajus/redux-immutable). This package provides its own implementation of `combineReducers` that knows how to iterate over an Immutable Map instead of a plain Javascript object. - ## Sharing data between slice reducers Similarly, if `sliceReducerA` happens to need some data from `sliceReducerB`'s slice of state in order to handle a particular action, or `sliceReducerB` happens to need the entire state as an argument, `combineReducers` does not handle that itself. This could be resolved by writing a custom function that knows to pass the needed data as an additional argument in those specific cases, such as: diff --git a/docs/recipes/structuring-reducers/StructuringReducers.md b/docs/recipes/structuring-reducers/StructuringReducers.md index 033524102fc..c02e477247f 100644 --- a/docs/recipes/structuring-reducers/StructuringReducers.md +++ b/docs/recipes/structuring-reducers/StructuringReducers.md @@ -21,7 +21,7 @@ It is vital that these Prerequisite Concepts are **thoroughly understood** befor #### [Prerequisite Concepts](PrerequisiteConcepts.md) -It's also important to note that some of these suggestions may or may not be directly applicable based on architectural decisions in a specific application. For example, an application using Immutable.js Maps to store data would likely have its reducer logic structured at least somewhat differently than an application using plain Javascript objects. This documentation primarily assumes use of plain Javascript objects, but many of the principles would still apply if using other tools. +Standard Redux architecture relies on using plain JS objects and arrays for your state. If you're using an alternate approach for some reason, the details may differ based on your approach, but many of the principles will still apply. ### Reducer Concepts and Techniques diff --git a/docs/understanding/history-and-design/PriorArt.md b/docs/understanding/history-and-design/PriorArt.md index 59fced569bf..d78d22877ce 100644 --- a/docs/understanding/history-and-design/PriorArt.md +++ b/docs/understanding/history-and-design/PriorArt.md @@ -15,7 +15,7 @@ Redux was inspired by several important qualities of [Flux](https://facebook.git Unlike Flux, **Redux does not have the concept of a Dispatcher**. This is because it relies on pure functions instead of event emitters, and pure functions are easy to compose and don't need an additional entity managing them. Depending on how you view Flux, you may see this as either a deviation or an implementation detail. Flux has often been [described as `(state, action) => state`](https://speakerdeck.com/jmorrell/jsconf-uy-flux-those-who-forget-the-past-dot-dot-dot-1). In this sense, Redux is true to the Flux architecture, but makes it simpler thanks to pure functions. -Another important difference from Flux is that **Redux assumes you never mutate your data**. You can use plain objects and arrays for your state just fine, but mutating them inside the reducers is strongly discouraged. You should always return a new object, which is easy with the [object spread operator proposal](../../recipes/UsingObjectSpreadOperator.md), or with a library like [Immutable](https://facebook.github.io/immutable-js). +Another important difference from Flux is that **Redux assumes you never mutate your data**. You can use plain objects and arrays for your state just fine, but mutating them inside the reducers is strongly discouraged. You should always return a new object, which can be done using the [object spread operator](../../recipes/UsingObjectSpreadOperator.md) or [the Immer immutable update library](https://immerjs.github.io/immer/). While it is technically _possible_ to [write impure reducers](https://github.com/reduxjs/redux/issues/328#issuecomment-125035516) that mutate the data for performance corner cases, we actively discourage you from doing this. Development features like time travel, record/replay, or hot reloading will break. Moreover it doesn't seem like immutability poses performance problems in most real apps, because, as [Om](https://github.com/omcljs/om) demonstrates, even if you lose out on object allocation, you still win by avoiding expensive re-renders and re-calculations, as you know exactly what changed thanks to reducer purity. @@ -31,7 +31,7 @@ Unlike Redux, Elm is a language, so it is able to benefit from many things like [Immutable](https://facebook.github.io/immutable-js) is a JavaScript library implementing persistent data structures. It is performant and has an idiomatic JavaScript API. -Immutable and most similar libraries are orthogonal to Redux. Feel free to use them together! +(Note that while Immutable.js helped inspire Redux, today we recommend [using Immer for immutable updates instead](../../style-guide/style-guide.md#use-immer-for-writing-immutable-updates).) **Redux doesn't care _how_ you store the state—it can be a plain object, an Immutable object, or anything else.** You'll probably want a (de)serialization mechanism for writing universal apps and hydrating their state from the server, but other than that, you can use any data storage library _as long as it supports immutability_. For example, it doesn't make sense to use Backbone for Redux state, because Backbone models are mutable. diff --git a/website/_redirects b/website/_redirects index 142583e9808..44dadac6b9c 100644 --- a/website/_redirects +++ b/website/_redirects @@ -35,6 +35,8 @@ /introduction/threeprinciples* /understanding/thinking-in-redux/three-principles:splat /introduction /introduction/getting-started +/recipes/using-immutablejs-with-redux* /style-guide/style-guide#use-immer-for-writing-immutable-updates +/recipes/usingimmutablejs* /style-guide/style-guide#use-immer-for-writing-immutable-updates /recipes/computingderiveddata* /recipes/computing-derived-data:splat /recipes/configuringyourstore* /recipes/configuring-your-store:splat @@ -43,7 +45,6 @@ /recipes/migratingtoredux* /recipes/migrating-to-redux:splat /recipes/reducingboilerplate* /recipes/reducing-boilerplate:splat /recipes/serverrendering* /recipes/server-rendering:splat -/recipes/usingimmutablejs* /recipes/using-immutablejs-with-redux:splat /recipes/usingobjectspreadoperator* /recipes/using-object-spread-operator:splat /recipes/writingtests* /recipes/writing-tests:splat @@ -109,13 +110,14 @@ /docs/api/compose.html* /api/compose:splat /docs/api/Store.html* /api/store:splat +/docs/recipes/UsingImmutableJS.html* /style-guide/style-guide#use-immer-for-writing-immutable-updates + /docs/recipes/ComputingDerivedData.html* /recipes/computing-derived-data:splat /docs/recipes/ReducingBoilerplate.html* /recipes/reducing-boilerplate:splat /docs/recipes/ImplementingUndoHistory.html* /recipes/implementing-undo-history:splat /docs/recipes/ServerRendering.html* /recipes/server-rendering:splat /docs/recipes/MigratingToRedux.html /recipes/migrating-to-redux /docs/recipes/UsingObjectSpreadOperator.html* /recipes/using-object-spread-operator -/docs/recipes/UsingImmutableJS.html* /recipes/using-immutablejs-with-redux:splat /docs/recipes/IsolatingSubApps.html /recipes/isolating-redux-sub-apps /docs/recipes/WritingTests.html* /recipes/writing-tests:splat /docs/recipes/StructuringReducers.html /recipes/structuring-reducers/structuring-reducers diff --git a/website/sidebars.js b/website/sidebars.js index c9710e91894..993065fc382 100755 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -49,7 +49,6 @@ module.exports = { 'recipes/computing-derived-data', 'recipes/implementing-undo-history', 'recipes/isolating-redux-sub-apps', - 'recipes/using-immutablejs-with-redux', 'recipes/code-splitting', 'recipes/troubleshooting', {