Artwork by RJ Barnes And how you too can build simple, predictable reducers with Flux Standard Functions TL;DR I wrote which is a package for building simple, predictable reducers for Redux. It helps make awesome things easy, like replacing reducer boilerplate with clear, validated, atomic mutations. It works great with both existing and greenfield projects. ⭐ ⭐ [flux-standard-functions](https://www.npmjs.com/package/flux-standard-functions) tiny (2.1kb) Give it a star! Redux is Hard Redux is the library that everyone loves to hate. Hardly does a day go by that I don’t run across some article claiming that “library X” is killing Redux. Why is Redux “hard” and why are developers so bent on “killing” it? Fundamentally, Redux exists to resolve the tension between “two concepts that are very hard for the human mind to reason about: mutation and asynchronicity.” It does so through a tradeoff with another potentially difficult concept: indirection. Such a tradeoff when approached correctly can yield really good results. However, if you think Redux is a silver bullet, you’re gonna have a bad time. “Redux is a tradeoff” — Dan Abramov But besides the complexity of indirection, Redux has another difficulty: its simplicity. One of the best features of Redux its lack of them. At the time of writing, the library weighs in at . Its small size is due to is lack of features and opinions. This simplicity makes Redux very flexible. only 2.6kb when minified and gzipped But ultimate flexibility can often be a barrier to productivity. Andrew Clark introduced a “human-friendly standard for Flux action objects” which he calls “ ” (or FSAs) to rein in the flexibility of actions. He observed that “It’s much easier to work with Flux actions if we can make certain assumptions about their shape.” The opinions introduced by FSAs help the developer reason about the shape of actions, however, there has still been a lack of similar standards for building reducers. Flux Standard Actions Much of the boilerplate of Redux reducers comes from the ubiquitous bespoke spreading of objects to create new states. Flux Standard Functions (FSFs) are a set of specific functions that can be composed to form reducers. By removing some of Redux’s ultimate flexibility, FSFs allow developers to build and understand simple, predictable reducers. When faced with a challenge like Redux, developers have a few options: improve the experience or “kill it” and use something else. In general, I find the Redux tradeoff worth it for large projects. So after looking through a ton of Redux reducers looking for common patterns, I think I have landed on a solution that is simple, comprehensive, productive, and backward compatible. Show me some code! Here is an example of a “Todo reducer” that is based on provided in the Redux docs: an example Here is the same example implemented with Flux Standard Functions: First, let’s take a look at the most obvious differences. To start with, the structure of a Todo ( ) is defined outside of the reducer. Doing so helps to prevent duplication as we add more Todo action types. Next, the resulting reducing function is todoDef really small! Second, we are actually getting a few things for free that weren’t present in the original example. If was undefined, the original reducer would create an invalid new state. In the “FSF” example, if the Todo is undefined, the function determines that the Todo is invalid and returns the original state by reference. This validation not only ensures that the application state stays valid, but also adds the performance optimization of not returning a new state object if nothing changes. id id set For a more apples-to-apples comparison (including validation), the original hand-written reducer would look more like this: The above example starts to show how even a very simple operation can start to bloat over time. All we did was add validation and suddenly we have the same Todo properties defined in multiple locations. As this reducing function evolves over time and as new action types are added, it will become difficult to ensure that the definition and validation of a Todo item remain consistent throughout the Todo reducer. This is just one example of simplifying a fairly trivial operation (adding a new item to state). Let’s take a look at the Three Functions to understand how they can be used to build pretty much any reducer you can think of. The Three Functions The Standard Functions work with a combination of three parameters: , , and . The is the data that is being "mutated". (Note: that if the is changed, then a shallow clone is created.) The is the new data that is being added, updated, replaced, or removed. The is an object that describes the structure of the object and is used for validation, indexing, and optimization. target payload definition target target payload definition target Set Set provides the ability to either add or overwrite data. This is analogous to the “Create” CRUD operation. If a value that is being set already exists, then it will be overwritten. If the value being set does not exist then it is added. Any operations that set a value not included in the or that are defined as immutable will be ignored. definition Use for single values and for batch operations. [set](https://github.com/skonves/flux-standard-functions/blob/master/src/functions/set.md) [setEach](https://github.com/skonves/flux-standard-functions/blob/master/src/functions/set-each.md) Patch Patch provides the ability to update (or “upsert”) data. This is similar to the “Update” CRUD operation. If a value being patched already exists, then it will be replaced. For complex properties, it will be partially updated with the properties in the . If the property did not already exist and is valid per the then it will be added. payload definition Use for single patches and for batch operations. [patch](https://github.com/skonves/flux-standard-functions/blob/master/src/functions/patch.md) [patchEach](https://github.com/skonves/flux-standard-functions/blob/master/src/functions/patch-each.md) Unset Unset provides the ability to remove data. This is analogous to the “Delete” CRUD operation. If the valued being unset exists, then it is removed. If the value being unset does not exist or is specified by the to be required or immutable, then nothing happens. definition Use to remove single values and for batch unset operations. unset unsetEach Definitions and Rules The Standard Functions use the parameter to validate changes. The function is used to create the definition for the types of objects in the application state. definition [define()](https://github.com/skonves/flux-standard-functions/blob/master/src/define.md) Here is an example of defining a “User” type: The following rules can be used to create type definitions: : Property is the “key” of an “Index” or table key() : The property cannot be changed once set immutable() : The property must be present required() : The property may be present or optional() undefined : The property in an “Index” of the provided definition indexOf(def) : The property is a complex object objectOf(def) : The property is a primitive array arrayOf() Note that Typescript allows developers to define types as well; however, Typescript types are a compile-time-only construct. Once the code as been transpiled down to vanilla Javascript, the type definitions themselves disappear. This means that they cannot be used at runtime for validation. Because of this, the function is designed to work independently of or in conjunction Typescript. If you are using Typescript, you are provided with rich type- and property-checking. If you are not using Typescript, then you still benefit from the runtime validation provided by the Standard Functions. define() Prior Art As with most new libraries, Flux Standard Functions stands on the shoulders of giants. Several projects have influenced the development of this one (or at least take a stab at solving a similar problem). I would be remiss to not mention them here. Flux Standard Actions “A human-friendly standard for Flux action objects. It’s much easier to work with Flux actions if we can make certain assumptions about their shape.” The Standard Functions are based on a similar philosophy that reducers are easy to build if we can make assumptions about their composition. Redux Data Normalization The principles of heavily influenced this project. Like the docs recommend, Flux Standard Functions work well with a “flat” or normalized state. The concept of “tables” loosely translates to “Indexes” in this project. I, too, recommend keeping state flat. YMMV. Data Normalization discussed in the Redux docs Underscore/Lodash The and functions map roughly to the and Standard Functions. There are a number of examples to be found online of using Underscore or Lodash for building reducers. This project provides a small subset of lodash-like functionality optimized for use with Redux. _.set _.merge set patch Normalizr “Normalizr is a small, but powerful utility for taking JSON with a schema definition and returning nested entities with their IDs, gathered in dictionaries.” Immer Immer is the hot new library that became popular during the development of Flux Standard Functions and is definitely worth a look. It is “a tiny package that allows you to work with immutable state in a more convenient way. It is based on the mechanism.” copy-on-write Angular 1 See this for more info. conference talk Like what you see? Hopefully, you find the project interesting! If so, here’s how you can help: Weird behavior? Confusing docs? Github issues are super helpful! 😃 Give it a Star: ⭐ https://github.com/skonves/flux-standard-functions Give this post a few 👏