Giter Site home page Giter Site logo

foxbunny / duckweed Goto Github PK

View Code? Open in Web Editor NEW
4.0 2.0 0.0 437 KB

JavaScript microframework for programming reactive interfaces using Model-Action-View architecture

License: MIT License

TypeScript 97.90% JavaScript 2.10%
framework-javascript snabbdom typescript elm-architecture

duckweed's Introduction

Duckweed logo

Duckweed: JavaScript microframework for programming reactive interfaces using Model-Action-View architecture

Travis build status Coverage status

Duckweed is inspired by Elm and Simon Friis Vindum's Functional Frontend Architecture. Unlike its sources of inspiration, though, Duckweed's primary goal is not to promote or enforce functional programming paradigm. It's main goal is to provide a simple API, and functions happen to be a good step in that direction.

Duckweed is written in TypeScript, and uses Snabbdom under the hood.

Although Duckweed is small (only 7KB min+gz), it includes state management, transitions, routing events, and the full power of Snabbdom.

Installation

To install Duckweed, use:

npm install duckweed

Currently, only beta releases are available.

Take it for a spin

This fiddle contains a small counter example that you can fork and play with.

Demo app

A more fully featured proof-of-concept application can be found on GitHub. You can see it in action here.

Documentation

Documentation can be read on GitHub. It includes a developer guide and the API reference.

I want class-based components!

Here you go:

class HelloDuckweed {
  constructor(initialState = {}) {
    this.state = initialState;
    this.update = this.update.bind(this);
    this.render = this.render.bind(this);
  }

  update(_, address, ...args) {
    return this[address](...args);
  }

  updateName(e) {
    this.state.name = e.target.value;
  }

  render({act}) {
    return (
      <div>
        <h1>Hello {this.state.name || "World"}!</h1>
        <input type="text" value={this.state.name} on-input={act("updateName")} />
      </div>
    );
  }
};

const comp = new HelloDuckweed();

duckweed.runner(undefined, comp.update, comp.render);

No, sorry, it was just a joke. I'm pretty sure it would work, but, to be honest, I designed Duckweed specifically to avoid using this pattern.

What's with the name?

Duckweed is a water plant with simple no-frills features, and one of the fastest growing plants on Earth. It has been argued that a single duckweed could create a mass of duckweeds the size of the Earth in a single month. It is also an invaluable water purifier, and being studied as a potential source of clean energy. What I'm trying to say is, duckweed is pretty awesome.

Benchmarks

It's... erm... fast enough. :)

Under construction

Duckweed is still in very very early stages of development. Don't expect it to be production-ready. The API may also fluctuate between versions. The level of breakage you can expect is predictable, though.

Duckweed uses semantic versioning, so no major problems should be expected until the next major version bump. Each time a major version is bumped, though, you can be certain it's backwards-incompatible with the previous versions.

License

Duckweed is licensed under the terms of the MIT license. See the LICENSE file for more information.

duckweed's People

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

duckweed's Issues

Remove custom events

Custom events are generally more specific to how we build applications, and should not be in the framework. They may be provided as separate modules but not as part of the Duckweed package.

Fix documentation for plugins: init takes state

The init() function in plugins takes the action function, and the initial model state. The documentation talks about this, but it says the following in the first sentence:

and the init property which is a function that takes a message handler as the only argument.

Simplify the `class` attribute

The class attribute now does something similar to what Vue does by accepting objects in addition to strings and arrays. Strings and arrays are able to cover all of the cases we may ever encounter, so there is no need to support the object version.

Allow event processors that return undefined

The duckweed.event.from() function expects the processor to always return an array. The undefined should be allowed as a return value which marks the event as 'unhandled' and suppresses the action.

This is useful, for example, when we want to handle an event like keypress only for certain keys and not for others. Currently, this decision is deferred to action handlers, which breaks the separation of concerns.

Remove patch()

Having sealed actions that invoke patch multiple times makes testing a wee bit harder than it should be. We need to mock the patch callback, and scoped patchers makes it even more involved. Not the mention the complexity of the duckweed internals.

Patchers originally solved the problem of asynchronous multi-step actions. For instance, we initiate an action, and then we set the state to, say loading = true, and perform some async action, after which we perform another patch to clear the loading flag and update the state using the response from the async call.

The action handler API can be changed so that the problem solved by patcher remains solved, while making testing much easier, and duckweed internals lighter. The new API could look like this:

const actions = {
  doSomething: (model) => 
    [
      {...model, loading: true}, 
      fetch('/foo')
        .then(processData)
        .then(data => ['finishLoading', data])
        .catch(() => ['fail'])
    ],
  finishLoading: (model, data) => 
    ({...model, loading: false, data: data}),
  fail: (model) =>
    ({...model, loading: false, error: true}),
}

Action handler would be able to return either a model object, or an array of object model and a promise. In the latter case, duckweed will assume that the promise will resolve to a message, and thusly allow a new action to be triggered after the async operation is finished.

This pattern can be further generalized so that any array with a length of two will be a pair of model and message, where message can be an array of address and data, or a promise. That allows possibly interesting patterns where a parent module can pass a message address to the child so that the child can 'call back' the parent when it's done, and so on.

The main drawback of this API design is that it prevents us from using arrays as models, but I think it's a worthwhile sacrifice. Apps using arrays can wrap the arrays in an object to work around this limitation.

Since we are messing with actions now, there is another design that might make it into 1.0.0, which is to use functions instead of objects to match and execute actions. The above example may look like this in that case:

const update = ({address, args}, model) => {
  switch(address) {
    case "doSomething":
      return [
        {...model, loading: true}, 
        fetch('/foo')
          .then(processData)
          .then(data => ['finishLoading', data])
          .catch(() => ['fail'])
      ]
    case "finishLoading":
      const [data] = args
      return {...model, loading: false, data: data}
    case "fail":
      return {...model, loading: false, error: true}
  }
}

Using a function would offer a great deal of flexibility compared to a simple mapping, but may require additional tools to make the result look better (e.g., not using switch, etc). This API may be offered as an alternative if the internal design can be kept simple.

This is, obviously, a backwards-incompatible change, but at this point, but really there are only three duckweed apps in existence (that I know of) so the damage should be minimal.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.