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

staction

v5.2.0

Published

A straightforward method of managing state, with support for Promises, Generators, and Async/Await.

Downloads

69

Readme

Staction

A straightforward method of managing state, with support for Promises, Generators, Async/Await, and Async Generators (asyncIterable).

Because sometimes all you really need is state and actions.

Build Status

Basic usage:

import Staction from 'staction'

let staction = new Staction()

let actions = {
  increment: ({ state, actions } , incrementAmount = 1) => {
    return {
      count: state().count + incrementAmount
    };
  }
}

let initialState = (actions) => {
  return {count: 0};
}

/*
   This method is called every time after state is updated.
   Useful for calling setState at the top of a React component tree.
*/

let onStateUpdate = (state, actions) => console.log(`count is ${state}`)

staction.init(
  actions,
  initialState,
  onStateUpdate
)

let incrementAmount = 5

/*
   all actions return a promise, that resolves with the updated state.
   This helps eliminate passing callbacks through, and also allows for a way
   to react to errors that occur in actions from outside the action.
*/
let result = staction.actions.increment(incrementAmount)

result
  .then(newState => console.log(newState))
  .catch(e => console.log(e))


console.log(staction.state) // state is {count: 5}

Actions

Actions should always yield/return the full new state, or a Promise that resolves to the new state. They can be regular, async, or generator functions, and async call order will be maintained! The state argument is a function that will always return the current state. Additional arguments passed when the action is invoked are passed after state and actions.

All of the following are valid actions.

const myActions = {
  action1: ({ state, actions }) => state() + 1,

  actions2: ({ state }) => {
    return Promise.resolve(state() + 1);
  },

  action3: function* ({ state, actions }) {
    yield state() + 1;
    // state() === 1

    yield state() + 1;
    // state() === 2
  },

  action4: function* ({ state, actions }) {
    yield state() + 1
    // state() === 1

    yield new Promise(resolve => resolve(state() + 1))
    // state() === 2

    // IIFE's come in really handy if you want to combine Generators and async functions.
    yield (async function(){
      return state() + 1
      // state() === 3
    }())
  },

  // ...Or just use Async Generators! Very handy for "isFetching" type state.
  action5: async function* ({ state }) {
    const nextState = await Promise.resolve(state() + 1);

    yield nextState;
  }
}

Typescript

Staction aims to provide great Typescript support. This includes maintaining types througout actions!

import Staction, { ActionParams } from 'staction';

type State = {
  foo: string;
};

const initialState: State = {
  foo: 'bar',
};

type Params = ActionParams<State, Actions>;

const actions = {
  action1: (params: Params, val: string): State => {
    return params.state();
  },

  action2: async (params: Params): Promsie<State> => {
    return params.state();
  },
};

// You may explicitly type the actions above, this is just a bit of
// a shortcut.
type Actions = typeof actions;

const store = new Staction<State, Actions>();

store.init(
  actions,
  () => initialState,
  () => {}
);

// The arguments of this action should be correctly typed when calling it.
store.actions.actions1('hello');

With React (and Typescript)

A "state down, actions up" style of configuration in a React component might look something like the following:


/*  appState.ts  */

export type AppState = {
  foo: string;
}

export const initialState: AppState = {
  foo: 'bar',
}

/* appActions.ts */

import { ActionParams } from 'staction';

type Params = ActionParams<State, Actions>;

const appActions = {
  noopAction: (params: Params) => {return state}
}

export type AppActions = typeof appActions;

/* appStoreContext.ts */

import React from 'react';
import Staction from 'staction';
import { AppState } from './appState';
import { AppActions } from './appActions';

const defaultStaction = new Staction<AppState, AppActions>();

export type AppStore = Staction<AppState, AppActions>;

export const AppStoreContext = React.createContext<AppStore>(defaultStaction);

/* App.ts */

import React, { FC } from 'react';
import Staction, { ActionParams } from 'staction';
import { AppStore, AppStoreContext } from './appStoreContext';
import { initialState, AppState } from './appState';
import { appActions, AppActions } from './appActions';

const MyComponent: FC () => {
  const [appStore, setAppStore] = useState<AppStore>(new Staction<AppState, AppActions>());
  const [currentAppState, setCurrentAppState] = useState<AppState>(initialState);

  useEffect(() => {
    appStore.init(
      appActions,
      () => initialState,
      (nextState) => setCurrentAppState(nextState)
    )
  }, []);


  return appStore.initialized ? (
    <AppStoreContext.Provider value={appStore}>
      {/* App Components */}
    </AppStoreContext.Provider>
  ) : null;
}
}

Staction instance methods

Logging

  • staction.enableLogging() - enable logging of each action call to console.
  • staction.disableLogging() - disable logging of actions to console.
  • staction.disableStateWhenLogging() - do not include current state when action logs.
  • staction.enableStateWhenLogging() - include current state in action logs.

Middleware

Staction supports basic middleware. They can be called pre or post action.


const staction = new Staction();

staction.setMiddleware([
  {
    type: 'pre', // or 'post,
    method: ({ state, name, args, meta }) => { /* do stuff here. */ },
    meta: {} // An object of user configurable meta data. 
  }
])