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

fnx

v0.0.32

Published

Work in Progress: Wickedly quick, stunningly simple, reactive state management.

Downloads

22

Readme


FNX aspires to be a rock-solid, production-ready, state management solution. Currently, however, FNX is in its early stages. Until v1.0.0 don't use it for anything critical. You can help FNX reach v1.0.0 by asking questions, finding bugs, and suggesting improvements.


Getting Started

Install

NPM Registry

yarn add fnx or npm install fnx --save

UMD Build

<script src="https://unpkg.com/fnx@[VERSION]/umd/fnx.min.js"></script>

<script src="https://unpkg.com/fnx@[VERSION]/umd/ReactiveComponent.min.js"></script>

(Make sure you replace "[VERSION]" with the NPM version of FNX you'd like to use - e.g. "0.0.32")

Starter Projects

Introduction

FNX is a robust state management library optimized for ease of use. It's kinda like Redux met MobX and had a baby. If you're familiar with either solution (or both) FNX shouldn't be too hard to grasp. FNX takes the best ideas from both these libraries and adds a few of it's own. What you end up getting is transparently reactive state management complete with:

The icing on the cake is easy integration with React through the ReactiveComponent api. In a way, FNX is kinda like React, but for data. It abstracts away the tedious parts of state management (like serialization and keeping your view in sync with your model) leaving you with more time to build awesome stuff.

As the new kid on the block, FNX makes no compromises in order to support older JavaScript environments. It started as part of an academic research project at Brigham Young University and legacy compatibility simply isn't part of the project's goals. Proxies, WeakMaps, and Symbols are among the ES6 features FNX uses internally to provide the best state management solution possible. The good news is that the vast majority of JavaScript runtime environments support these features today (think pretty much everything minus IE11 and older). The bad news is that if you need support for older environments FNX isn't for you.

Simple Example

Here's a basic React app using most of FNX's features. Checkout the TypeScript setup page or Babel setup page to learn how to properly configure your project to work with FNX. ReactiveComponent and all properties of fnx are documented in the API Reference section.

import fnx from 'fnx'
import ReactiveComponent from 'fnx/react'
import React from 'react'
import ReactDOM from 'react-dom'

let nextTodoId = 0

// Describe the state tree

class TodoModel extends fnx.Model {
  @fnx.readonly id = fnx.number
  text = fnx.string
  completed = fnx.boolean

  @fnx.action
  toggleComplete() {
    this.completed = !this.completed
  }
}

class AppModel extends fnx.Model {
  filter = fnx.string
  todos = fnx.mapOf(fnx.object(TodoModel))

  @fnx.computed
  getVisibleTodos() {
    const todoArray = Object.keys(this.todos).map(id => this.todos[id])
    if (this.filter === 'completed') {
      return todoArray.filter(todo => todo.completed)
    } else if (this.filter === 'uncompleted') {
      return todoArray.filter(todo => !todo.completed)
    } else {
      return todoArray
    }
  }

  @fnx.action
  changeFilter(filter) {
    this.filter = filter
  }

  @fnx.action
  addTodo(text) {
    this.todos[nextTodoId] = {
      id: nextTodoId, text, completed: false
    }
    nextTodoId++
  }
}

// Initialize the state tree

const app = new AppModel({
  filter: 'none',
  todos: { }
})

app.use(logger)

function logger(next, action) {
  console.log(action.path.join('.'), action.args)
  next()
}

// Components

const Todo = ReactiveComponent(({ todo }) => {
  return <div onClick={ () => todo.toggleComplete() }>
    { todo.completed ? <s>{ todo.text }</s> : todo.text }
  </div>
})

const TodoList = ReactiveComponent(() => {
  return <div>
    { app.getVisibleTodos().map(todo => {
      return <Todo key={ todo.id } todo={ todo }/>
    }) }
  </div>
})

class TodoComposer extends ReactiveComponent {
  render() {
    return <div>
      <input ref={ c => this.inputEl = c }/>
      <button onClick={ () => this.addTodo() }>Add</button>
    </div>
  }

  addTodo() {
    app.addTodo(this.inputEl.value)
    this.inputEl.value = ''
  }
}

class FilterControl extends ReactiveComponent {
  render() {
    return <div>
      Filter
      <select value={ app.filter } onChange={ event => this.handleChange(event) }>
        <option value='none'>None</option>
        <option value='completed'>Completed</option>
        <option value='uncompleted'>Uncompleted</option>
      </select>
    </div>
  }

  handleChange(event) {
    app.changeFilter(event.target.value)
  }
}

const App = ReactiveComponent(() => {
  return <div>
    <h1>Todos</h1>
    <FilterControl/>
    <TodoComposer/>
    <TodoList/>
  </div>
})

// Render the app

ReactDOM.render(<App/>, document.querySelector('#app'))

Transparent Reactive Programming?

If that sounds hard and confusing to you, rest assured you are not alone. The good news is that it's really a lot simpler than it seems. In reactive programming, reading data is the same as subscribing to change notifications. You don't need to explicitly declare you want to rerun a function when something changes – the function will run again automatically.

There are a lot of libraries out there designed to make reactive programming easier. One such library is RxJS. Libraries like RxJS, however, are not transparent. The transparent part means you can strip away all the fancy API calls typically needed to make reactive programming work. ES6 Proxies enable FNX to apply all of those methods behind the scenes without you needing to grok an entirely new API just to perform simple mutations. Transparent reactive programming gives you the advantages of reactive programming without the obtuse APIs that often accompany it. Actually, you won't need to change hardly anything about how you program. Simply mutate data inside of FNX actions. Transparency means you can use a utility library like Lodash with no issues. Reactivity means FNX is silently taking notes of what's going on and making sure your computed values and view are kept up-to-date with your data.

Contributing

The biggest way to contribute to FNX currently is through feedback. The API is still in flux and the internals are likely to undergo a major rewrite soon. The current version of the source code is a good first draft but needs restructuring to allow some performance enhancements and to make it easier to contribute to. Join the gitter channel or open an issue to give feedback or ask questions. Suggestions and new ideas are welcome!