Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: [FEATURE] Working on the new v4 docs #111

Open
wants to merge 1 commit into
base: 4-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions config/mallery.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,26 @@ module.exports = {
public: "../docs/public"
},
colors: {
accent: "#222"
accent: "#37A"
},
toc: [
{ title: pkg.name, path: "README.md" },
{ title: "Home", path: "README.md" },
{ path: "introduction.md" },
{ path: "getting_started", children: [{ path: "installation.md" }] },
{ title: "Changelog", path: "CHANGELOG.md" },
{ title: "Repository", href: pkg.repository.url }
{ title: "Repository", href: pkg.repository.url },
{
path: "legacy",
title: "Legacy (v2)",
children: [
{ title: "Getting Started", path: "README.md" },
{ path: "installation.md" },
{ path: "first_steps.md" },
{ path: "stores.md" },
{ path: "reducers.md" },
{ path: "subscriptions.md" },
{ path: "streams.md" }
]
}
]
};
43 changes: 43 additions & 0 deletions docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Introduction

My goal with alo always was to simplify state management and to improve reusability without requiring immutability. As of today, writing this article, its about 3 years ago since I developed the first couple versions of alo including v2.8.3. After releasing v2.8.3 and working with many alternative libraries, I started to realize several conceptual weaknesses and reached a dead end in its implementation, I had to rethink my strategy.

I write this introduction as a mix of history-article to share my past experience developing a state management library, and as conceptual-guide into alo. And by the way, please don't hang me for my bare-bones english :).

My audience for this article is mostly frontend developers that already do state management in some form. But if something is very unclear, just ask me with a [Github Issue](https://github.com/alojs/alo/issues), I am open to extend the article also for a wider audience.

## TLDR

TODO: Transactions!!! / Change-propagation

Reading about state management one mostly hears, that immutability is the best and only way to it, especially coupled with React where the performance of the application is directly depending on fast equality checks. While not often talked about there are alternatives and depending on your use case they might actually work better than the immutability approach. Everything summarized, I recommend a thinking of (Im)-mutability as tools for different jobs, instead of just following one paradigm like a religion.

While the state management in javascript is mostly filled with solutions based around immutability, I try to provide a library to fill the other gap. Not to replace existing solutions, but to broaden the way we think in our frontend work. Sorry for the "Please not again a new library"-camp :P

## Mutability and Immutability

Advantages of immutability:
- "Transactions": Since you don't mutate objects but rather create new ones, references to the old object don't autmatically get updated. This lets you control when, and how state changes are being propagated through your application
- "Equality checks": You can use equality checks "==" on object references. This is especially useful when combined with a object state tree, where after a change somewhere deep in the state, each parent object in the tree is recreated, allowing you to very fast compare state regarding deep changes without the need of recursive equality checks.
- "Easy undo,redo / time travel": Thanks to its characteristics it's comparatively easy and very efficient to implement undo, redo features in your application and gain access to devtools like time travel debugging.

Advantages of mutability:
- "Comes for free in the language": Most basic constructs in the language are based around mutability. So while experimenting and prototyping your mind stays more focused on the actual business-problem you're trying to solve, instead of always keeping an eye and making sure that your precious objects aren't mutated.
- "One way to do it": Goes hand in hand with the first argument. You don't have to think about if you gonna use some library, object destructuring, Object.freeze, Object.assign. Just use that mighty "=" :)
- "List change performance": Especially if your use case has to do with large lists and frequent item changes, mutability will serve you very well because it doesn't come with the overhead of recreating the whole array on every change.

I recommend immutability-based state management for mid-large, team based projects where its important that state changes are only propagated at will. In such a scenario its wise to use something like [Redux](https://github.com/reduxjs/redux) and [Immer](https://github.com/immerjs/immer) to access the experience of a very large community.

For small-mid projects, performance-depending features and prototypes I recommend to spend atleast some time with mutability because it might

## Why another library?

## Deepclone approach

## Command approach

## Event/Tagging approach

## Getter/Setter approach


68 changes: 68 additions & 0 deletions docs/legacy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
What follows is a backup of the original Alo v2.8.3 documentation.

Alo is a state management library, helping you to organize frontend application state in a productive way, giving you ways to handle side effects, and allowing you, to stay in control of whats going on. There are many libraries to manage state: [Redux](https://github.com/reactjs/redux), [Mobx](https://github.com/mobxjs/mobx), [Vuex](https://github.com/vuejs/vuex), and a countless number more. Although I like the ideas behind them, I wasn't quite happy how they introduce many necessary extra steps to get started, require dozens of extra plugins to get you your job done, or introduce new babel build tools, just to write pragmatic code.

## Features

* State managed with [flyd](https://github.com/paldepind/flyd) streams (therefore being able to access the state stream directly if needed)
* All the features of a basic state management library (stores, dispatch, subscriptions, middleware)
* Computed properties with dependencies on the state (only computed if dependencies change)
* Dependency-handling for subscriptions (allowing to call subscriptions only if specific dependencies change)
* Before and after events for subscriptions
* Flexible object oriented classes for stores, subscriptions, reducers, etc.
* Promise Support based on [yaku](https://github.com/ysmood/yaku) for dispatches, dependencies and middleware

## And how?
The [getting started](http://www.alojs.com/getting_started/first_steps.html) section will provide you with additional information, but here is Alo at first glance:

([Fiddle](https://jsfiddle.net/katywings/tr1vexgp/))
```js
var Alo = require('alo');
// Alo building blocks are always assigned to a specific Alo instance
var alo = new Alo();

// Stores hold the state of your app and are the main building block of Alo
var counterStore = alo.createStore({ count: 0 }, 'counter');

var increase = function(amount) { return { type: 'increase', payload: amount || 1 }}

// Reducers define how actions are applied to the state
var reducer = counterStore.createReducer(function(state, action) {
if (action.type === "increase") {
state.count += action.payload;
}

return state
});

// Subscribe to state changes
var subscription = counterStore.subscribe(function(stores) {
console.log('count', stores.counter.state.count);
});

// Apply the increase action to the store, you always will get a promise from this
var promise = counterStore.dispatch(increase(3));

// -> count 0
// -> count 3
```

## Roadmap
- Documentation of Todo example
- Writing a changelog
- Extending the documentation
- Promise code refactoring
- Where possible, reduce the library size

### Long-term
Subject to change and open for discussion!

- 100% Test coverage
- Dev-tools
- Remove utility dependencies
- Remove flyd streams: they didn't really bring an advantage and just added weight
- Remove subscription members: like with the flyd integration, my experience with them was, that they don't provide a lot of advantages
- Reducers just as functions
- Probably for 4.0: Subscriptions should not be called on addStore / removeStore - one can manually call remember if such behaviour is needed
- Probably for 4.0: Remove polymorphism, extend API with separate functions

74 changes: 74 additions & 0 deletions docs/legacy/first_steps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# First steps

Alo is very similar to the [flux pattern](https://medium.com/hacking-and-gonzo/flux-vs-mvc-design-patterns-57b28c0f71b7).

The main building blocks are:

- Stores: Are boxes of data, this data can be anything - from booleans to objects
- Reducers: They describe how an action should be synchronously applied to data
- Dependencies: Computations which are derived from data
- Subscriptions: Are reactions after the data in one or multiple stores has changed
- Middlewares: They can change an action - (even asynchronously), before it will be redirected to reducers

Except a more object oriented approach, there is one *essential difference* compared implementations like [Redux](http://redux.js.org/): Alo doesn't try to enforce the use of only one central store for your data, therefore subscriptions can be attached to multible stores. That is, you still can use it with a single store model if you want.

Alo's primary goal is not performance, but to minimize data mutation bugs. Therefore reducers will always get a clone and not the reference of the dispatched action and payload.

The data flow is as follow:
`dispatch on store -> middlewares -> reducers -> subscriptions`

## Relations
Thanks to the object oriented nature, the API consists of many helper functions. As an example stores and subscriptions are related [m:n](https://stackoverflow.com/questions/3397349/meaning-of-nm-and-1n-in-database-design#3397384):

([Fiddle](https://jsfiddle.net/katywings/oogL9bnr/1/))
```js
var makeLogState = function(storeName) {
return function(stores) {
console.log(stores[storeName].state);
};
};

var alo = new Alo();
var store = alo.createStore('initial state', 'store1');
var logStateStore1 = makeLogState('store1');
var subscription = store.subscribe(logStateStore1);
store.removeSubscription(subscription);
var subscription2 = alo.createSubscription(logStateStore1);
subscription2.addStore(store);

var store2 = alo.createStore(true, 'store2');
var logStateStore2 = makeLogState('store2');
var subscription3 = alo.createSubscription(logStateStore2);
store2.addSubscription(subscription3);
```

## Alo instances
As Alo follows a relational model, its building blocks are related to each other but they are also related to a Alo instance!

```js
// A single Alo instance
var alo = new Alo();
// This store is related to the alo instance above
var store = alo.createStore('state', 'storeName');
```

**This implementation detail was added, so that [debugging and tooling](https://github.com/alojs/alo/issues/9) of your apps can be achieved at a later moment - currently the alo instances are just here because they are here.**

If you are creating an app with Alo you should create the Alo instance at one place and use [dependency injection](https://youtu.be/6YBV1cKRqzU), to enable its use in multible parts of your software:

```js
var createAppStore = function(alo) {
return alo.createStore({}, 'app');
}
var App = function() {
var alo = new Alo();
var appStore = createAppStore(alo);
}
```

### When to use multible instances
[Debugging and tooling](https://github.com/alojs/alo/issues/9) will be per instance, therefore if you are developing an "App in app" solution you could create an instance per App. Use multible Alo instances when you want to completely separate specific components for debugging.


## Utilities
In the example code you probably will find many uses of the `alo.util` property. Use these with precaution! There are plans to remove many of them in a future major release.
32 changes: 32 additions & 0 deletions docs/legacy/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Installation

## Versions
There are different main files available for use in the [dist](https://github.com/alojs/alo/tree/master/dist) folder.

The files are constructed with a combination of these postfixes:

* full: includes extras (helpers like some basic reducers and middleware)
* dev: enables long stacktraces in promises and will (as of 3.0) add the debug console to the Alo class

## CDN's
You can use Alo with NPM or Bower or even straight from a CDN. There are couple browser builds available in the dist folder

### Development (unminified and with devtools) (see <a href="#versions">Versions</a>)

* Core: https://cdn.rawgit.com/alojs/alo/v2.8.3/dist/alo.dev.js
* Full: https://cdn.rawgit.com/alojs/alo/v2.8.3/dist/alo.full.dev.js

### Production (minified)

* Core: https://cdn.rawgit.com/alojs/alo/v2.8.3/dist/alo.min.js
* Full: https://cdn.rawgit.com/alojs/alo/v2.8.3/dist/alo.full.min.js

### More?
Please have a look at [RawGit](https://rawgit.com). It allows you to use basically any file from Github repos (and therefore also all the files from https://cdn.rawgit.com/alojs/alo/v2.8.3/dist/)

## Use (CJS)

* Core: `require('alo/dist/alo.js')`
* Core *Dev*: `require('alo/dist/alo.dev.js')`
* Full: `require('alo')`
* Full *Dev*: `require('alo/dist/alo.full.dev.js')`
2 changes: 2 additions & 0 deletions docs/legacy/middlewares.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Middlewares
To be continued...
46 changes: 46 additions & 0 deletions docs/legacy/reducers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Reducers
Reducers apply actions to store states:

[Fiddle](https://jsfiddle.net/katywings/z2hheeuj/1/)
```js
var alo = new Alo();
var store = alo.createStore(0, 'number');

// Create a reducer on alo instance level
var reducerPlus = alo.createReducer(function(state, action) {
if (action.type == '+') {
state += 1;
}

return state;
});
// Add the reducer to the store
store.addReducer(reducerPlus);

// This creates and adds a new reducer to the store in one step
var reducerMinus = store.createReducer(function(state, action) {
if (action.type == '-') {
state -= 1;
}

return state;
});

alo.util.Promise.resolve()
.then(function() {
return store.dispatch({ type: '-' });
})
.then(function() {
return store.dispatch({ type: '-' });
})
.then(function() {
return store.dispatch({ type: '+' });
})
.then(function() {
return store.dispatch({ type: '-' });
})
.then(function() {
console.log(store.getState());
// -2
})
```
95 changes: 95 additions & 0 deletions docs/legacy/stores.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Stores
As earlier mentioned stores hold the state of your application and redirect actions to reducers.

They can be created like this:

```js
var alo = new Alo();
var store = alo.createStore("state", "storeName");
```

The constructor parameters are:

1. Initial state of the store, (can be any type but generally will be an object)
2. Optional: An unique name for the store (this name will be used as store identifier in the subscription!)

## Dispatch
Dispatches are the way to notify a store about a state change:

```js
var action = {
type: 'changeState',
payload: 'newState'
}
store.dispatch(action);
```

*Atleast if you are not using a middleware to change this behaviour, you **always**
need to dispatch action objects atleast with a type field.*

The dispatch will however not do anything, as long as the store doesn't have a related reducer!
If the store has any reducers, they will decide what should happen with your action.

## Get the current state
- Only the state:
```js
store.getState();
```

- State and computed properties
```js
store.getData();
```

## Computed properties
Alo has built-in support for computed properties. They are only recalculated, when their dependencies change (plusThree is only called, when plusTwo changes). Look at the following example to get a feel for them:

[Fiddle](https://jsfiddle.net/katywings/w4q6242q/4/)
```js
var alo = new Alo();
var store = alo.createStore(1, 'number');
store.createReducer(function(state, action) {
if (action.type == 'add') {
state += 1;
}

return state;
});

// Creates 3 computed properties, plusThree is especially dependent on plusTwo
store.createComputedProperty({
'plusOne': function(store) {
return store + 1;
},
'plusTwo': function(store) {
return store + 2;
},
'plusThree': [['plusTwo'], function(store, computed) {
return computed.plusTwo + 1;
}]
});

store.subscribe(function(stores) {
// Computed
console.log('state', stores.number.state);
console.log('computed properties', stores.number.computed);
});

store.dispatch({ type: 'add' });
```

**At the moment computed properties will (as lazy as they are) only be called after a dispatch with state change, as an example you could have a "initialize" action which sets the initial store state**:

```js
var alo = new Alo();
var store = alo.createStore({}, 'number');
store.createReducer(function(state, action) {
if (action.type == 'initialize') {
state = action.payload;
}

return state;
});
// store.createComputedProperty...
store.dispatch({ type: 'initialize', payload: 1});
```
Loading