Necto
Necto compliments Redux by providing a composable, declarative api to create flows through redux (Action -> Reducer or Action -> Saga). The intent of Necto is to reduce boilerplate, simplify and standardize action creators, and group action logic so that your team can write and build new features in Redux faster and more efficienly at scale.
Key Goals
- It should work alongside an existing Redux implementation.
- It should work with Redux Devtools.
- Action creators should still uphold the Three Principles of Redux and follow Flux Standard Action patterns.
- Action creators should describe a behavior or a user interaction, but make sure the business logic of the implementation is still traceable and reusable.
- Dispatching an action method should be consistent and every action should take the same arguments.
- Reduce the amount of boilerplate needed to build new slices of state and write new features.
- Radically simplify asynchronous flows and remove the painful setup of sagas.
- Actions should only ever trigger a single side effect - A reducer or a saga.
How does Necto solve these issues?
In the wild west of Redux land, there are many ways to structure your Redux project. The following is the most basic and likely most commonly used method of structuring a redux action, reducer, and asynchronous saga.
Old Way
// users/index;;;;
// users/constants.js SOME_ACTION: 'USERS/SOME_ACTION' SOME_OTHER_ASYNC_ACTION: 'USERS/SOME_OTHER_ASYNC_ACTION';
// users/actions.js;const someAction = type: ConstantsSOME_ACTION foo; const someOtherAsyncAction = type: ConstantsSOME_OTHER_ASYNC_ACTION bar;
// users/reducer.js; const INITIAL_STATE = foo: 'bar' bar: 'foo'; { };
// users/sagas.js;;; { try let bar = action; // Some saga logic here catch e {}} { ;} ;
Necto Way
Creating a Necto Instance
; const users = 'users' initialState: foo: 'bar' bar: 'foo' ; users; users;
Using a Necto Instance
; store;/* Dispatched Action { payload: { foo: 'someFooChange', }, meta: { someMeta: 'someMeta', }, type: '[USERS/SOME_ACTION] User clicked some button', _actionType: 'USERS/SOME_ACTION', _interaction: 'User clicked some button', _requiredParams: { payload: ['foo'], meta: null } } Next State { foo: 'someFooChange', bar: 'foo', }*/ store;/* Dispatched Action { payload: { bar: 'some data!' }, meta: undefined, type: '[USERS/SOME_OTHER_ASYNC_ACTION] User fetched some data', _actionType: 'USERS/SOME_OTHER_ASYNC_ACTION', _interaction: 'User fetched some data', _requiredParams: { payload: ['bar'], meta: null } } Console : What data? some data */
Use
Install
$ npm install --save necto
Using Necto With Redux
// store/some_model.jsconst someModel = 'someModel';;
Connecting to Your Store
// store/combine_reducers.js; ; // other reducers ...SomeModel // returns {'someModel': someModel.Reducers} // other reducers (even non-necto!) reducers; /* Store at @@INIT is { someModel: initialState }*/
Connecting to Your Saga Middleware
// store/combine_sagas.js;; ; { ;}
Concepts
Reducer Key / Reducer Slice / Name
- This key or name is the slice of the store that sits on the top level
Flow
- A flow is an
action => flowPath
combination - Flows are analogous to a redux action and can be dispatched just like a Redux action, however they imply that based on an action, one of two things occur: 1) A reducer updates some part of the store or 2) An asynchronous saga is started
Flow Path
- A flow path is either a reducer function or an asynchronous saga function.
Saga
- An asynchronous path that is triggered from the dispatch of an action
- https://redux-saga.js.org/
- Necto mostly makes use of takeEvery and takeLatest in redux-saga, however also lets you specify your own "watcher" function to customize any saga flows.
API
new Necto(name, [options])
Arguments
name
(String): Name of your reducer key[options]
(Object):initialState
(any): The initial state for this slice of the store
createFlow(name, flowPath, [options])
Arguments
-
name
(String): Action function name (only valid function names allowed) -
flowPath
(Function): defined as eitherfunction (state, action)
ORfunction*(action)
. Necto checks the actual names of the arguments that are passed into this function and decides what type of flow path to create. e.g.function (a, b)
will throw an error on flow creation.- Reducer Function: Takes the paramters
(state, action)
and should return a new slice of the state - Saga Generator Function: Takes the parameter (action), should be a generator function (
function*() {}
) and can handle any asynchronous code that a normal saga function can.
- Reducer Function: Takes the paramters
-
[options]
(Object): OptionalrequiredParams
(Object or Array {String} or Function):payload
(Array {String}): Any required payload parameters specified as dot-notation, like lodash.get. Validates if key exists and is not null or undefined.meta
(Array {String}): Any required meta parameters specified as dot-notation, like lodash.get. Validates if key exists and is not null or undefined.- **
requiredParams
can also be provided as an array of strings or a function that returns any of these options['payload.foo','meta.bar']
(action) => { if (action.foo) return ['payload.bar']}
-
interactionRequired
(Boolean): Optional
// Object patternnecto; // Array patternnecto; // Function patternnecto;
Other
What does Necto mean?
Verb nectō (present infinitive nectere, perfect active nexī, supine nexum); third conjugation
- I bind, tie, fasten, connect, interweave, attach; unite; relate.
- I bind by obligation, oblige, make liable.
- I contrive, devise, compose, produce.
Future Plans
- TODO...