Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
TL;DR
I wrote flux-standard-functions which is a tiny (2.1kb) 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. â 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 only 2.6kb when minified and gzipped. Its small size is due to is lack of features and opinions. This simplicity makes Redux very flexible.
But ultimate flexibility can often be a barrier to productivity. Jason Kurian (@JaKXz on Github) introduced a âhuman-friendly standard for Flux action objectsâ which he calls âFlux Standard Actionsâ (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.
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 an example provided in the Redux docs:
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 (todoDef) 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 really small!
Second, we are actually getting a few things for free that werenât present in the original example. If id was undefined, the original reducer would create an invalid new state. In the âFSFâ example, if the Todo id is undefined, the set 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.
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: target, payload, and definition. The target is the data that is being "mutated". (Note: that if the target is changed, then a shallow clone is created.) The payload is the new data that is being added, updated, replaced, or removed. The definition is an object that describes the structure of the target object and is used for validation, indexing, and optimization.
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 definitionor that are defined as immutable will be ignored.
Use set for single values and setEach for batch operations.
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 payload. If the property did not already exist and is valid per the definition then it will be added.
Use patch for single patches and patchEach for batch operations.
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 definition to be required or immutable, then nothing happens.
Use unset to remove single values and unsetEach for batch unset operations.
Definitions and Rules
The Standard Functions use the definition parameter to validate changes. The define()function is used to create the definition for the types of objects in the application state.
Here is an example of defining a âUserâ type:
The following rules can be used to create type definitions:
- key(): Property is the âkeyâ of an âIndexâ or table
- immutable(): The property cannot be changed once set
- required(): The property must be present
- optional(): The property may be present or undefined
- indexOf(def): The property in an âIndexâ of the provided definition
- objectOf(def): The property is a complex object
- arrayOf(): The property is a primitive array
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 define() 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.
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.
â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.
The principles of Data Normalization discussed in the Redux docs 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.
The _.set and _.merge functions map roughly to the set and patch 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.
â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 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 copy-on-write mechanism.â
Angular 1
See this conference talk for more info.
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 đ
How I replaced Redux with Redux was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.