Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

attadux

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

attadux

Implementation of the redux-modular-ducks, forked from the extensible-duck implementation and extended to include spected validators, state machines, helpers, and to follow a broader/looser definition of constants

  • 0.1.18
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
4
decreased by-78.95%
Maintainers
1
Weekly downloads
 
Created
Source

Attadux

An implementation of the modular redux proposal, forked from the extensible-duck implementation with added features to support state machines and object validators (using the spected tool). Attadux depends only on Ramda (or on libraries which only depend on Ramda) and also provides an alternate implementation (in Ramda, of course) of Reselect's createSelector() function.

Installation

npm install attadux

Usage

import {createDuck} from 'attadux'

const dux = createDuck({/* options */})

Options

When instantiating a Duck you'll pass a single prop, which is just an Object containing one or more of the following props:

  • namespace - (required) A String value representing the module/application you are building (ie, todo-app)
  • store - (required) A String value corresponding to a particular section of the Redux store (should match whatever you would normally name your reducer in your combineReducers())
  • types - An Array of String values which represent the actions you intend to create and dispatch throughout your application (don't worry about making the string too long/unique; your action types will all be formatted automatically as <namespace>/<store>/MY_ACTION_TYPE, which should prevent collisions)
  • consts - An Object (or a Function returning one) containing any simple primitive-ish values that your application may use (and which do not fall into any of the other categories of props to feed into your Duck instance). Mostly this will be String, Number and other simple values (Date, RegExp, Boolean). Even an Array is an acceptable prop to place inside the consts. Arrays are converted into an Object whose values match the values from your array, but whose keys are stringified representations of the values. Only Date, String, Number, Boolean or RegExp are acceptable in your array, in part because their stringified value will still be unique.
  • reducer - A Function that modifies/re-shapes your store in response to a specific type of action dispatched in your application. You can write your reducer in the way you've always written them in Redux, but with the added benefit of the Duck instance is provided as the third prop (the first two props provided to any Redux reducer are always state and action, respectively). You can leverage types, state machines, consts or anything else on your Duck instance, which (hopefully) makes the code you write simpler or more powerful.
  • initialState - Usually an Object (or a Function returning one). Sometimes people demonstrate examples where the initialState of the Redux store is a primitive value, but certainly isn't as common. This represents the initial condition you want this section of your Redux store to have.
  • selectors - An Object of scalar functions (or a Function returning an Object of them) returning a single value from the Redux store (no matter how deeply nested). Most often you use these for the first argument to the Redux connect() function, which maps the store to your component's props. These functions are memoized for performance and it's become common to leverage a tool like Reselect to facilitate this process, however a simple, copycat version of Reselect's createSelector() function (implemented in Ramda) is provided for your use (it's a named export you can import directly from attadux)
  • machines - An Object of Objects (but nothing nested deeper than that second Object). Each of those nested objects is a possible "state" for your component (or your application as a whole, if you want to use just one state machine to cover the entire app). Your component (or app) can be in only one state at a time and that "current state" is a String value that will be automatically populated when your dispatched Redux action forces a change to a different state. For the nested object: the key is the unique name for that state and the value (which is an object) contains all the transitions to which that state is allowed to change. When representing those allowed state transitions the key must match a Redux action type (ie, "LOGIN_USER_SUCCESSFUL", "LOGIN_USER_ERROR", etc.) and the value must match a the name of one of the other states defined for that machine. State machines may be a little confusing the first time you encounter the topic (and specifically how you might graft it into your Redux ecosystem), but the author of the stent library wrote up a great article which might help clarify how state machines could work in JavaScript. However Attadux has not implemented state machines using Stent nor have state machines been grafted into Redux in the way that author or others have attempted. The redux-machine library is the closest to the way state machines have been implemented in Attadux, which just sets a "status" prop to represent the current/new state whenever your reducer is invoked.
  • creators - An Object of action-creator functions (or a Function returning an Object of them) which return a plain 'old JavaScript Object representing the action to be dispatched to your reducer(s). It must contain a type prop.
  • enhancers - An Object of action-enhancer functions (or a Function returning an Object of them) which return a plain 'old JavaScript Object representing the action to be modified prior to hitting the reducers (in the Redux middleware chain). These enhancers are generic enough that you don't have to use them in Redux, as then simply modify an object containing (at least) a type prop The syntax for writing the enhancers is to define (what looks like) just an object whose keys represent names of props on the original action or new ones to be created on it. You can use the enhancer function to modify existing props (formatting, etc.) or create new props from existing ones. The values you set on the enhancer function's spec object are usually functions that make those modifications to existing props (or creates new ones), however you can also set values that are not functions, which will cause them be be passed right through onto the modified object. See shapey for further examples on this type of API. One last caveat to these Action enhancers is that you don't (and shouldn't usually) set a type prop on the enhancer function's spec object, however if you do so, the behavior of the enhancer changes to create a new object with only the fields you named in the spec object (the default behavior of the enhancer function is to merge the result of the enhancement back onto the original action).
  • multipliers - An Object of action-multiplying functions (or a Function returning an Object of them) which return an Array of one or more plain 'old JavaScript Object. One action in and one or more (new ones) are created from it. Again, either a single new object to create (from input passed in) or many new objects to create from it. When using this in Redux middleware, you can apply a fanout behavior to a dispatched Redux action (ie, in response to some kind of "post login" action, you can create several new actions to go an retrieve one-time lookup data, new that the user is authenticated). You even have access to enhancements for the new objects, as each new object you spec out can have a function as one of its props, which will follow the same re-shaping logic as discussed for the enhancers.
  • queries - An Object whose values are an Array of 2 values [Function, String] (similar to the format for validators). In this case the function operates on the string value of the query itself. If you use GraqphQL queries, the first arg would be just the instance of a function (could even be a third-party lib, like the gql function from the graphql-tools and the react-apollo packages). The second arg would be the string template literal that constitutes the query. The last param is always the actual String value of the query. query itself. When two params are passed into the query builder The first param is defaults to identity when it isn't provided
  • validators - An Object of "validators" (or a Function returning an Object of them). This feature requires some explanation, as it is not part of the boilerplate developers are accustomed to in Redux application. The simplest use for a validator is to use it in your reduxForm() function call (if you do use redux-form) and it will return an object where valid and invalid values are represented with true and and Array of error messages, respectively. This feature leverages an outstanding, simple library called spected. Spected is a simple, small, yet powerful tool (built using only Ramda) and it curries your validation schema. Which means you can extract that curried validator from the Duck instance and run it as often as you wish against any input that must match your schema. Also, the author of spected built a form validation library on top of spected, called Revalidation which you can leverage instead of Redux Form, if you find its API to be more suited to how you compose front-end components.
  • validationLevel - If you have set your validators and you've named any of them to match a redux action type, they will be applied in the middleware chain to validate the action's payload according to one of four possible strategies:
    • LOG - will always pass through a dispatched action but will append a validationErrors prop to the payload if any validations fail
    • PRUNE - will always pass through a dispatched action but will remove any invalid fields from the payload
    • CANCEL (default) - stops the middleware chain when validations fail on a dispatched action
    • STRICT - will only pass validated payloads whose action types are listed as inputs for the current state of a given state machine

Also, if you wish to track machine states on a prop other than states (at the root of the initialState object), provide an alternate value for the stateMachinesPropName prop (defaults to 'states'). This value can be any one of the following:

  • a single string value representing the prop name to place at the root of the initialState object (ie, 'status')
  • an array of string values representing a nested path (ie, ['user', 'login', 'currentState']
  • a single dot-separated string value representing the nested path (ie, 'user.login.currentState')

Middleware

The middleware currently has one purpose, to validate any of the dispatched redux actions. You can, of course, use validators for many things - like user form input - but if you give a validator the same name as a redux action type, then you can use it to validate a redux action. All you need to do (aside from naming your validator appropriately) is apply the attadux middleware when you configure your redux store.

//store.js

import {createStore, applyMiddleware} from 'redux'
import {createValidatorMiddleware} from 'attadux'

import initialState from './initialState'
import reducers from './reducers'
import allDucks from './ducks'

export default createStore(
    reducers,
    initialState,
    applyMiddleware(createValidatorMiddleware(allDucks))
)

And to create the "row" of ducks (very similar to combineReducers() for your Redux reducers:

// ducks.js

import {createRow} from 'attadux'
import authDuck from './components/auth/duck'
import products from './components/products/duck'
import customers from './components/customers/duck'
import ordersDuck from './components/orders/duck'

export default createRow(authDuck, products, customers, orders)

Note: It doesn't matter what alias you give your duck when importing, because createRow() will use the .store prop from each duck (which is a String) to format the row as:

{auth, products, customers, orders}

State Machines & Validators

The main difference between this library and the extensible-duck predecessor is the addition of support for State Machines and Validators. These can be used in the obvious way (extracting them from the duck instance and using them manually in your reducer(s)) but they can also be used in a unique, almost automatic manner whenever your reducer processes a dispatched action. To be used in such a manner means Attadux makes a couple of assumptions about your Redux store (but if those assumptions mismatch the way you write your Redux application you can use them manually in the manner mentioned above). It's expected that the state passed into your reducer is an object, and in all but the rarest and simplest cases this is how your store is shaped anyway. It's also expected that the state machine can represent the "current state" somewhere in your Redux store (the machine itself is not kept there) and by default Attadux will set a prop called states (which is an Object whose values are the "current state" representation for each of your named state machines).

Keywords

FAQs

Package last updated on 08 Jun 2018

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc