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

relessjs

v0.0.1

Published

Simplified alternative of Flux architecture using function calls as 'emitting implicit actions'

Downloads

4

Readme

Reless-js

A state management library, inspired by Flux architecture and the Redux implementation.

This implementation slightly deviates from the Flux spec, and doesn't work with actions and dispatching of actions. In Reless you can semi-directly call a reducer with some payload, while still keeping a single state, the unidirectional dataflow and possibilities to use the Redux-devtools.

Concepts

Flux

Flux architecture is build around unidirectional data-flow.

You dispatch an action, that's is being reduced on the current state. After all reducers finished doing their job, the listeners are called to notify that there was a potential change in the state.

Maintenance with Redux

skip to Relessjs (to the rescue) if you want to know what Reless can do for you.

The main thing that troubles me is the maintenance when using Redux. (If you know Redux and want to skip to the real deal of this library: )

When creating actions you're not just calling dispatch({ type: 'INCREMENT_COUNTER' }), but you would define a constants file where you define all actions:

module.exports = {
  incrementCounter = 'INCREMENT_COUNTER'
}

Now you can call use the constant instead of typing it, and it's in one place to maintain. The result:

let constants = require('./constants.js')

dispatch({ type: constants.incrementCounter })

But wait, we might have a very complex action, where, based on some properties we'd like to convert it into another action. So let's create an actionFactory, and let's not forget to import the constants:

let constants = require('./constants.js')

module.exports = {
  increment: (someData) => {
    return { 
      type: constants.incrementCounter, 
      data: someData.map(x => x.toLowerCase()) 
    }
  }
}

But wait, we haven't even talked about the reducer yet:

let constants = require('./constants.js')

module.exports = {
  counter: (state, action) {
    if (typeof state === 'undefined') {
      return { ...state, counter: 0 }
    }
    switch (action.type) {
      case constants.incrementCounter:
        return { ...state, counter: state.counter + 1 }
      case constants.decrementCounter:
        return { ...state, counter: state.counter - 1 }
      default:
        return state
    }
  }
}

This process is a bit cumbersome. If you want a bit of functionality you have to change 3 files and this grows when you have more and bigger actions and reducers. Maybe this is not a problem for a big enterprise-like application but for something small, you'd like something more to the point. This is where Relessjs can help.

Relessjs (to the rescue)

Have a look here to see the basic scenarios test/showcase

So... You still want to use a single state, but looking at these 4 files, Redux might be overkill. Now you can use Relessjs to create a store, call reducers "directly" and update the state in a unidirectional flow.

With Relessjs we can give you exactly that, but with less files and no constants. Your reducer is your action. So you call your reducer, and watch the state change.

Example without and with state in the reducer

Let's give a simple example of a counter:

  • setCountToOne: call a reducer
  • setCount: call a reducer with a payload
  • incrementCounter: call a reducer, and use the previous state

The simplest form of the reducer is of type payload => (state | (state => state)).

The reducer can be called with a payload. Then you can directly return a state or return a function of type state => state. When you return a function from the state, it'll be called with the current state. You can use this state to increment a counter.

let store = new Reless({
  state: { counter: 0 },
  reducers: { 
    setCountToOne: () => ({ counter: 1 }),
    setCount: (count) => ({ counter: count }),
    incrementCounter: () = state => ({ counter: state.counter + 1 }),
  },
})

store.reducers.setCountToOne()
store.state.counter // 1

store.reducers.setCount(4)
store.state.counter // 4

store.reduces.incrementCounter()
store.state.counter // 5

Example with asynchronous call in reducer

If you want to do something asynchronous, your reducer should be of type payload => state => reducers => void

Here the reducers get passed on. You can call them inside another reducer. This way we can keep track of the reducer calls.

We've had some prior art, where we passed an update function, but we experienced an issue with that: you cannot log what is happening since you only pass a new state instead of calling a reducer. In terms of Redux, you want to dispatch an action and not directly set a state, so that you have a nice trail of actions that are dispatched in order to see where bugs are happening.

let store = new Reless({
  state: { counter: 0 },
  reducers: { 
    setLoading: loading => ({ loading })
    doAsync: () => () => (reducers) => {
      reducers.setLoading(true)
      setTimeout(() => {
        reducers.setLoading(false)
      }, 1000)
    }
  },
})

store.reducers.doAsync()
store.state.loading // true
// one second later
store.state.loading // false

Asynchronous example using the newest state

When returning a function in the reducer call, it'll be called with the latest state. This will allow you to create a countdown based on the last state.

The function passed to the reducer has the following type: state => state.

  let store = new Reless({
    state: { counter: 3 },
    reducers: {
      setCounter: (counter) => ({ counter })
      countdown: () => () => reducers => {
        let interval = setInterval(() => {
          reducers.setCounter(state => { 
            if (state.counter === 1) {
              clearInterval(interval)
              return 0
            }
            return state.counter - 1
          })
        }, 1000)
      },
    },
  })
  // start with 3
  store.reducers.countdown()
  // directly after calling the reducer
  store.state.counter // 3
  // first second passes
  store.state.counter // 2
  // second second passes
  store.state.counter // 1
  // third second passes
  store.state.counter // 0