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

react-helix

v1.0.2

Published

A minimal library for Flux-like architecture.

Downloads

11

Readme

react-helix

npm

A minimal library for Flux-like architecture.

Motivation

  • Makes Model to be immutable.
  • Makes the border of Model and View to be clearly.
  • No boilerplate. Minimal requisite.
  • Doesn't block the server-side rendering.

Overview Helix

                               This has the Application Model
                              +-------------------------------
                             /
                       +---------------------+
                       | The Root Component  |
                   +---| (Application class) |<<-+
                   |   +---------------------+   |
                   |                             |
Distribute changes |                             | Send actions
  via Virtual DOMs |                             | via Event Bubblings
                   |                             |
                   |   +---------------------+   |
                   |   |  Detail Components  |   |
                   +->>|                     |---+
                       +---------------------+
                                       /
     Those accept user's interactions /
    ---------------------------------+

First, I named this Flux-like design. It's Helix. This section explains about Helix.

Helix inherits the most basic concept of Flux, Unidirectional Data Flow, and provides better way that works immutable data.

Three elements

Helix has three elements:

  • Model is immutable data structures.
  • Action is domain logics, they are just functions that transforms from the previous Model to the next Model.
  • View is appearance, they show the Model and send Action by user operations.

It's important, Action is just a function.

Two dataflows

Since Model is immutable, the root component has ownership of the application model uniquely. But the model will be updated by detail components (they would not know the root component). Helix has two symmetric flows as solution of this gap.

  • Downward Flow is to distribute changes via Virtual DOMs (diff & patch).
  • Upward Flow is to send actions via Event Bubbling.

Let's see the flow of the whole.

When a necessity to update Model was occurred by user operations, then a detail component fires a send action event with an Action and parameters. The event carries the action and parameters to the root component via event bubbling. (Upward Flow).

The root component has the application model. When it received a send action event, it takes Action and parameters from the event, then it applies the Action with the parameters to its model. And the root component distributes changes via Virtual DOM (Diff & Patch). (Downward Flow).

Thus, the root component keeps ownership of the application model, and detail components can determine details of updating completely. And we don't need boilerplates (e.g. declares constants, registers handlers to Dispatcher/Store, ...), and and it's scalable to increase Actions.

Overview react-helix

react-helix is a library for Helix, provides two classes (and mixins).

  • StageComponent (or StageMixin) has an ability to catch Actions that sent from its descendant, and apply the Action to its state automatically. This is an implement for the root component.
  • AgentComponent (or AgentMixin) has an ability to dispatch bubbling events to send Actions. This is an implement for detail components.

For example:

  • Action Definition:

    export function removeTodoItem(model, id) {
      return model.withItems(items =>
        items.filter(item => item.id !== id)
      );
    }
  • Send Action:

    onRemoveButtonClick(/*event*/) {
      const id = this.props.value.id;
      this.request(removeTodoItem, id);
    }

That's almost all.

See Also Examples

Installation

npm install react react-helix

Usage

StageComponent / StageMixin

declare class StageComponent extends React.Component {
  constructor(props: any, stageValuePath: string = "");

  stageValue: any;
  setStageValue(value: any, callback?: () => void): void;

  filterAction(event: SendActionEvent): boolean;

  // When you would override those methods, must call `super`.
  componentDidMount(): void;
  componentWillUnmount(): void;
  componentWillUpdate(): void;
  componentDidUpdate(): void;
}

const StageMixin = {
  stageValue: any;
  setStageValue(value: any, callback?: () => void): void;

  // stageValuePath: string = "";
  //   You can define `stageValuePath` property.
  //   The property is used from `componentWillMount` in order to define
  //   `stageValue` and `setStageValue`.

  // filterAction(event: SendActionEvent): boolean
  //   You can define `filterAction` method.
};

stageValuePath

StageComponent has one parameter stageValuePath. This string is a path to stage value in its state.

For example, when you set model to stageValuePath, then the component saves its stage value into this.state.model. Other example, when stageValuePath is myapp.model, then the component saves its stage value into this.state.myapp.model.

stageValue

stageValue is a getter property to get the value of stageValuePath.

setStageValue

setStageValue is a method to set the value of stageValuePath.

filterAction

filterAction is a method to determine whether it should handle the action.

  • event.action is the action.
  • event.arguments is an array of arguments for the action.
  • If returned false, this component ignores the action.

By default, always returns true.

AgentComponent / AgentMixin

declare class AgentComponent extends React.Component {
  constructor(props: any);
  request(action: (stageValue: any, ...) => any, ...args: any[]): void;
}

const AgentMixin = {
  request(action: (stageValue: any, ...) => any, ...args: any[]): void;
};

request

request is a method to send an action. action is a function. args is an array of arguments for action.

You can replace this method to a spy for unit tests. User interactions will trigger this method in the end.


Helix in depth

More Actions (together Promise / Generator)

react-helix allows actions return a promise or a generator. In this case, StageComponent treats the return value specially.

Implementation of react-helix does NOT depend on Promise/Generator. Thus react-helix works even if those are not implemented. However, if application want to use Promise/Generator, will require Polyfills.

Promise

If the return value is a promise, StageComponent waits for the promise fulfilled, then sets the result to its stage value. If the result of the promise is a function, StageComponent calls the function with its stage value immediately, and sets the result of the function to its stage value.

function promiseAction(model) {
  // ↑ This model is a instance at the time of this action was called.
  return hogeAsync()
    .then(function() {
      // ↓ This model2 is a instance at the time of this promise became
      //   fulfilled.
      return function(model2) { ... };
    });
}

Generator

If the return value is a generator, StageComponent advances it until done. While advancing, StageComponent treats yielded values.

  • If undefined was yielded, just ignores it.
  • If a function was yielded, StageComponent calls the function with its stage value immediately, and sets the result to its stage value.
  • If a promise was yielded, StageComponent backs the result of the promise (it is similar to co).
  • If a generator was yielded, it is processed recurcively.
  • Otherwise, StageComponent sets the value to its stage value.
  • yield (excludes case of give a promise) backs the current stage value.
function* generatorAction(model) {
  // ↑ This model is a instance at the time of this action was called.

  // Lonly `yield` just returns the current model.
  const model2 = yield;

  // Give an object to `yield`, it updates model, and returns new model.
  const model3 = yield model.withStatus(1);

  // Give a promise to `yield`, it returns the result.
  const threeSevens = yield Promise.resolve(777);

  // Give a promise (will be rejected) to `yield`, it throws.
  try {
    yield Promise.reject(new Error());
  }
  catch (err) {
    //...
  }
}

The Action with a generator is useful to implement complex business logics.

Nested StageComponent

Actions is carried by event bubbling, it will handled at the first StageComponent. At this time, the StageComponent confirms whether it should handle the action to this.filterAction().

        +-------+
        | Stage |<---------+
        +-------+          |
         /     \           |
       +--+   +--+         |
       |  |   |  |         |
       +--+   +--+         |
      /   \      \         |
  +--+   +--+   +-------+  |
  |  |   |  |   | Stage |<-+ If filterAction() determines ignore, more bubbles.
  +--+   +--+   +-------+  |
  /      /      /     \    |
+--+   +--+   +--+   +--+  |
|  |   |  |   |  |   |//|--+
+--+   +--+   +--+   +--+

This will allow us to use nested StageComponent.

Server-side Rendering

Event bubbling to carry actions does not work in server side. But, react-helix does not prevent rendering (maybe. NEEDS TEST).

Browser Compatibility

Implementation of react-helix is using:

  • Object.defineProperties
  • Function.prototype.bind
  • EventTarget.prototype.addEventListener
  • EventTarget.prototype.removeEventListener

Thus IE8 and older are not supported.