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

revault

v0.6.0

Published

Yet another state management library

Downloads

34

Readme

Revault

The state management library you've been waiting for

Build Status codecov npm npm JavaScript Style Guide MIT License

Table Of Contents

  1. Why Revault?
  2. Usage
  3. Docs

Why Revault?

Redux is great and without doubt has helped push the web forward by providing a strong mental model around global state. Lately however, a few things have started to frustrate me when using Redux:

  1. Repetition. The boilerplate, just for the most simple task, has become wearisome.
  2. Logic. Business logic is spread out. Some logic lives in the action, some lives in the reducer, and yet some more lives in the component.
  3. Middleware. Apart from logging and handling promises, middleware is more of a hassle than a help.

A few libaries have been released recently that have attempted to solve these issues. Two to note, and mainly where inspiration for Revault was drawn, are Unstated and Statty. Both libraries use a basic component with render props to access global state ❤️ - but each had shortcomings.

Unstated's Container works well to control business logic and encapsulate several pieces of state, all while feeling very familiar to Component local state. Containers, otherwise known as Stores in Revault, live on, only accessed differently on render.

Statty's approach to access is great - by using a select prop to pluck only the pieces of state you want, it's easy to inject derived state as a render prop, while also making it easy to check for referential equality to prevent unecessary renders. Statty was only missing a dedicated logic unit.

Thus, Revault was born - marrying the concepts of Unstated and Statty in what looks to be a happy union. Hope y'all like it! 😎

Usage

Begin by creating your first store, which extends from Store:

import { Store } from 'revault';

export default class TodoStore extends Store {
  state = {
    input: '',
    entries: [],
  };

  updateInput = input => {
    this.setState({ input });
  }

  addTodo = todo => {
    // setState acts like React component's setState,
    // meaning that it runs asynchronously and can also be passed an updater function.
    this.setState({
      entries: [...this.state.entries, todo],
    });
  }
}

Next, wrap your application with the Provider and pass in your stores.

import { render } from 'react-dom';
import { Provider as VaultProvider } from 'revault';
import * as stores from './stores';

/*
  `stores` may look something like the following. The key's will be as
  identifier's when accessing the store during render.
  {
    todos: TodoStore,
    ...etc
  }
*/

const App = () => (
  <VaultProvider stores={stores}>
    <Entry />
  </VaultProvider>
);

render(<App />, window.root);

Finally, drum roll please 🥁, use the Connect component to access our vault on render:

import { Connect } from 'revault';

export default () => (
  <Connect
    select={(stores) => ({
      todos: stores.todos.state.entries,
      input: stores.todos.state.input,
      updateInput: stores.todos.updateInput,
      addTodo: stores.todos.addTodo,
    })}
  >
    {({ todos, input, updateInput, addTodo }) => (
      <>
        <ul>
          {todos.map(todo => (
            <li>{todo}</li>
          ))}
        </ul>

        <form onSubmit={addTodo}>
          <input value={input} onChange={updateInput} />
          <button type="submit" >
            Submit
          </button>
        </form>
      </>
    )}
  </Connect>
)

You can also use the connect HOC if you need to perform more complex logic in component methods:

import React, { Component } from 'react';
import { connect } from 'revault';

@connect((stores) => ({
  todos: stores.todos.state.entries,
  input: stores.todos.state.input,
  updateInput: stores.todos.updateInput,
  addTodo: stores.todos.addTodo,
}))
export default class TodoList extends Component {
  render() {
    const { todos, input, updateInput, addTodo } = this.props;

    return (
      <>
        <ul>
          {todos.map(todo => (
            <li>{todo}</li>
          ))}
        </ul>

        <form onSubmit={addTodo}>
          <input value={input} onChange={updateInput} />
          <button type="submit" >
            Submit
          </button>
        </form>
      </>
    )
  }
}

You've done it! You have your first todo app up and running 3 simple steps.

Docs

<Provider>

Make the vault available to <Connect> via context

props

stores

object | required

A hash of stores. The key will be used as the accessor name when selecting state. The value is your Store constructor.

vault

object

Alternatively, you can pass in a preinstantiated vault. This is helpful during testing.

logger

function(oldState: object, newState: object)

Use the inspector prop during development to log state changes.

revault comes with a default logger inspired by unstated-debug.

import logger from 'revault/logger';

<Provider
  stores={{
    todos: TodoStore,
  }}
  logger={logger}
/>

<Connect>

Connect is a PureComponent that observes pieces of state and re-renders only when those pieces of state update.

props

select

function(stores: object, state: object) | defaults to s => s | returns object

Selects the slice of the state needed by the children components.

lifecycle

object

Access lifecycle methods of <Connect>. Each method has the same signature as select - so they will be passed stores and state. Comes with support for:

  • didMount
  • didUpdate
  • willUnmount

Often, we need to do work in the lifecycle methods but that can be difficult when using functional components. lifecycle makes it easy to kick off async work when mounting or performing cleanup when unmounting.

<Connect
  select={() => ({})}
  lifecycle={{
    didMount(stores) {
      stores.users.fetch(id);
    },
    willUnmount(stores) {
      stores.users.cleanup();
    }
  }}
/>
render

function(state: object) | required

The render fn is passed the observed state returned by select. You can also use a child function.

LICENSE

MIT License © Kyle Alwyn