npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

necto

v1.0.23

Published

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

Downloads

93

Readme

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
import Constants from './constants';
import * as Actions from './actions';
import Reducer from './reducer';
import Sagas from './sagas';
// users/constants.js
export default {
  SOME_ACTION: 'USERS/SOME_ACTION',
  SOME_OTHER_ASYNC_ACTION: 'USERS/SOME_OTHER_ASYNC_ACTION',
};
// users/actions.js
import Constants from './constants';
export const someAction = foo => ({
  type: Constants.SOME_ACTION,
  foo,
});

export const someOtherAsyncAction = bar => ({
  type: Constants.SOME_OTHER_ASYNC_ACTION,
  bar,
});
// users/reducer.js
import Constants from './constants';

const INITIAL_STATE = {
  foo: 'bar',
  bar: 'foo',
};

export default (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case Constants.SOME_ACTION:
      return {
        ...state,
        foo: action.foo,
      };
    default:
      return state;
  }
};
// users/sagas.js
import regeneratorRuntime from 'regenerator-runtime';
import { put } from 'redux-saga/effects';
import Constants from './constants';

function* someOtherAsyncAction(action) {
  try {
    let { bar } = action;
    // Some saga logic here
  } catch (e) {}
}

export function* watchSomeOtherAsyncAction() {
  yield takeEvery(Constants.SOME_OTHER_ASYNC_ACTION, someOtherAsyncAction);
}

export default all([fork(watchSomeOtherAsyncAction)]);

Necto Way

Creating a Necto Instance

import Necto from 'necto';

const users = new Necto('users', {
  initialState: {
    foo: 'bar',
    bar: 'foo',
  },
});

users.createFlow(
  'someAction',
  (state, action) => ({
    ...state,
    foo: action.payload.foo,
  }),
  {
    requiredParams: {
      payload: ['foo'],
      meta: null,
    },
  }
);

users.createFlow(
  'someOtherAsyncAction',
  function*(action) {
    try {
      let { bar } = action.payload;
      // Any saga logic here
      console.log('What data?', bar);
    } catch (e) {}
  },
  {
    requiredParams: {
      payload: ['bar'],
    },
  }
);

Using a Necto Instance

import users from './path-to-users-necto';

store.dispatch(
  users.Actions.someAction(
    'User clicked some button',
    { foo: 'someFooChange' },
    { someMeta: 'someMeta' }
  )
);
/*
  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.dispatch(
  users.Actions.someOtherAsyncAction('User fetched some data', {
    bar: 'some data!',
  })
);
/*
  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.js
const someModel = new Necto('someModel');
export default someModel;

Connecting to Your Store

// store/combine_reducers.js
import { combineReducers } from 'redux';

import SomeModel from './some_model';

export default combineReducers({
  // other reducers
  ...SomeModel.getReducers(), // 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
import regeneratorRuntime from 'regenerator-runtime';
import { all } from 'redux-saga/effects';

import SomeModel from './some_model';

export default function* rootSaga() {
  yield all([
    // other sagas
    SomeModel.getSagas(),
    // other sagas
  ]);
}

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 either function (state, action) OR function*(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.

    1. Reducer Function: Takes the paramters (state, action) and should return a new slice of the state
    2. 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.
  • [options] (Object): Optional

    • requiredParams (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 pattern
necto.createFlow('name', flowPath, {
  requiredParams: {
    payload: ['test'],
  },
});

// Array pattern
necto.createFlow('name', flowPath, {
  requiredParams: ['payload.test'],
});

// Function pattern
necto.createFlow('name', flowPath, {
  requiredParams: action => {
    return action.meta.foo && action.meta.bar ? ['payload.bang'] : null;
  },
});

Other

What does Necto mean?

Verb nectō (present infinitive nectere, perfect active nexī, supine nexum); third conjugation

  1. I bind, tie, fasten, connect, interweave, attach; unite; relate.
  2. I bind by obligation, oblige, make liable.
  3. I contrive, devise, compose, produce.

Future Plans

  • TODO...