
Show HN: Cell.js – A Self-constructing web app framework - gliechtenstein
http://www.celljs.org/index.html?hn
======
dgreensp
This is a thought-provoking new framework, thank you! It's novel and easy to
understand.

Having worked on sophisticated React apps, I've come to understand the
benefits and shortcomings of its architecture.

One insight is that properly encapsulating UI components and their behaviors
leads directly and naturally to nested state machines. React is cleanest if
you have one state machine, or N independent global state machines, that drive
your app. Once you start trying to compose state machines to make a
sophisticated UI, you end up in setState/callback hell, and then you try the
Redux/Elm model, in which states are composed to make bigger states. I think
it's fruitful to experiment with ways of composing state machines in which you
don't just glue the states together, but you let the machines interact. Of
course you still want to keep your program easy to reason about and avoid
spaghetti, but I'm sure there are interesting patterns lurking there.

Another issue is components in React aren't supposed to address each other.
You can easily call methods on your children using refs, though this is
discouraged and interacts badly with the update cycle and higher-order
components. Components not talking directly to each other is a great feature
right up until it's not, such as when you have to deal with focus and
selection changes. You're forced to contemplate taking what you thought of as
pure UI state -- local to a component and tied to that component's lifecycle
-- and moving it outside of React into a store whose structure parallels the
component tree (which in turn parallels the DOM), just so that you can
traverse it in a reasonable fashion and update it synchronously while DOM
updates remain asynchronous. In Cell.js, these three trees become one.

~~~
gliechtenstein
Thank you, I think you pretty much nailed it!

This architecture may not be very obvious to many people at this point but I
believe it can enable a lot of interesting use cases (as well as being able to
deal with even traditional app structures in more efficient and cleaner ways)

One example is my own use case, where I'm working on a web version of
Jasonette [https://www.jasonette.com](https://www.jasonette.com) I couldn't
find any existing framework that can achieve what I was trying to without
becoming very complex.

Thank you for the encouragement, you made my day :D

~~~
dgreensp
Sure thing. :) One thing I'm not completely clear on is the reactivity model.
It seems like setting $components (for example) triggers a setter that updates
the DOM? What about other DOM properties? What about underscore properties? Is
there a dependency graph under the hood, and can you set up a property to
depend on a calculation based on another property? Or is all data propagation
explicit/imperative/"push"?

~~~
gliechtenstein
Yup, it works for all $ variables.

As for the "_" variables they are meant to have no side effect on the DOM
directly and instead only trigger a function called $update() automatically,
which is where you can deal with all the view stuff.

So the only thing you need to remember is to:

1\. Declare whaterver "_" or "$" attribute you want to monitor initially on
the genotype.

2\. Once you have done that, every time you make a change to the underscore or
$ variables, they automatically get queued up (via Nucleus) and trigger the
$update() function right before the next render frame so they can be drawn all
at once in a single frame.

There are different ways of doing this, you could directly touch the DOM
attributes (without any prefix) or the $ variables and they will update the
view from anywhere.

But personally my pattern when using Cell to write apps is to NOT directly
touch the $ variables everywhere because it can be confusing, but instead keep
the underscore variables as sort of a "model", and only touch those. And
because updating a _ variable triggers an $update(), I can deal with all the
view related stuff (changing the DOM attributes and $ attributes) in one
place, inside $update(), which is the "reactive" feature. (But it's really up
to you how you do it) You can learn more here
[https://github.com/intercellular/tutorial#2-update](https://github.com/intercellular/tutorial#2-update)

Hope this helps!

p.s.

This document may be helpful in understanding the source
[https://github.com/intercellular/cell/blob/develop/GENESIS.m...](https://github.com/intercellular/cell/blob/develop/GENESIS.md)

~~~
dgreensp
Got it.

I immediately had a bunch more questions, some of which I tried to answer by
playing around with the demo.

Is "this" the actual DOM element? (Yes.) Then how is "context inheritance"
implemented? The section named "How Cell implements 'context inheritance' does
not ever say how it's implemented ;) (Getters and setters are set up by the
framework.) It feels like inheriting _everything_ would be a little messy
compared to specifying what goes in context.

Does changing something in "context" trigger updates in nested components that
read that context? Apparently, no.

Does assigning to $components always give you brand-new components, or is
there any sort of diff/patch as in React? Apparently, always new components
and new DOM nodes, so don't recalculate your children; you should probably
access them via the DOM API and call methods on them instead. So you don't get
"props flowing down" as in React.

How are assignments to underscored props coalesced, e.g. using what notion of
equality? What should you call props that you don't want to cause an $update?

Why is it ok to call methods on other cells from $update, causing further
updates, when this is frowned on in React, partly because of timing issues? I
sort of wonder if making state changes synchronous is a huge win and solves a
lot of issues; a lot of people are wondering if React makes a big mistake
there. At the same time, it seems like there would be situations where you
update underscored properties on A and B, and A is actually an ancestor of B,
so updating A would destroy B. Then you wonder if the framework is going to be
smart about the update order, and smart about dealing with defunct components
during the update cycle. Edit: That's not even a good example of a timing
issue caused by cascading updates. Can't think of a good one off the top of my
head.

Also edit: I haven't read the code, but it looks really short.

------
ezekg
Interesting. Though not really clear why you continuously say "no API!" when
you _have_ an API--it's just in the data itself via your "special keywords"
i.e. $cell, $update, etc. That's kind of like saying React components have no
API since you never actually call e.g. componentDidMount directly.

------
teyc
Interesting, but I get worried every time I see that code refer to specific
DOM id. It's probably ok for simple programs where there is only a handful of
elements. When the program gets larger, you will still need architectural
pieces to coordinate data.

You've put a lot of work into this, and there are a lot of examples that are
helpful to anyone who wants to evaluate it. I'm curious what is the motivation
behind this design - besides zero framework ? - is this in-use in production
any where?

~~~
gliechtenstein
Thanks! Zero framework is just one of the distinct traits that makes Cell
special.

Here's why I started working on Cell: Aside from Cell, I am working on a
project called Jasonette [http://jasonette.com/](http://jasonette.com/) which
lets you build build cross platform iOS/Android native mobile apps by writing
just a JSON markup. I have recently started working on a web version of
Jasonette, so that a single JSON markup can run exactly the same on iOS,
Android, and the Web. I tried implementing it with most of the existing
popular JS frameworks but found that they don't fit the bill. They are too
complex to fit Jasonette's philosophy of placing simplicity and ease of use as
top priority. Also Jasonette is great for decentralized apps, but the
centralized nature of all existing frameworks and approaches wasn't compatible
with what I was trying to achieve.

Cell doesn't just make things easier, but is designed fundamentally different
from traditional MVC frameworks. I think this image does a good job of
explaining: [https://s3-us-
west-2.amazonaws.com/fm.ethan.jason/domtree.jp...](https://s3-us-
west-2.amazonaws.com/fm.ethan.jason/domtree.jpg) Instead of creating a
centralized control mechanism, Cell lets you inject M-V-C (or anything else
you want to) directly into each HTML element, thereby decentralizing the
control. I think this will enable a lot of creative things and design patterns
going forward (which I'll demonstrate first with Jasonette-Web).

As for using querySelectors, I totally get it, because that was my initial
feeling as well. Initially I also felt like accessing elements directly was a
step backwards (because we've become accustomed to the "new" approach where we
keep a separate data structure that binds with the DOM, and directly accessing
the DOM feels like what we used to do with jQuery)

But Cell's approach brings its own benefits. First, as I mentioned the control
logic can be decentralized. Second, all the complexities of model-view binding
goes away because the very concept of binding existed because they were
separate. In case of Cell, the element can "contain" its own
model/view/controller, and because it contains them there's no need for
binding, which is one reason why Cell can stay simple. Another factor is this
architecture effectively turns each element into an app execution container of
its own, so these components can be extremely modular and portable.

You mentioned when the app becomes larger we'll need some architectural pieces
to coordinate data, and you are right, except that the same logic applies here
too. The "architecture" is the DOM tree itself. So the root element can
contain the root model, and the descendants can access it as well as keep
their own version of the model, so forth. I tried my best to explain all these
concepts on the homepage but I do realize it's a long read, so I hope this
explanation makes enough sense. TLDR: it requires a bit of stepping back and
reframing what we've been accustomed to but I'm confident this new approach
brings a lot of benefits that weren't easy to implement before.

~~~
mnishihan
How does it scale? Anti pattern is sometimes useful & obvious, but going with
anti patterns for everything is a stupid decision IMO. Frameworks are for
greater good. If you don't need it, don't use any of them. But when "No
framework" is listed as a key Mantra for yet another js library, I simply find
it as dumb & full of madness. :-/

~~~
gliechtenstein
What I meant by "No framework" was that there is no "Framework API" to
implement. Cell itself is a framework, so I agree that frameworks are for
greater good.

As for your question on how it scales, I don't see a reason why it shouldn't
scale. In fact I think this approach is more scalable than any existing
centralized approaches to building web apps because you can create complexity
out of simplicity.

p.s. The "No framework" part is just one of the benefits that arise as a side
effect of Cell's decentralized architecture. I did my best to explain why I
built this on the homepage. I know it's long but I hope you take a look at it
once more, it's worth a read even if you don't use the framework because Cell
does bring something new to the table. Hope this makes sense!

------
hannofcart
After having looked through the tutorials, I am unclear about the following:

1) How does one cell send messages to another. I suppose this is left to the
application?

2) How do you test code written in Cell.js? It appears to me that this is
going to be hard to do?

3) Is it intended that the entire webapp code be a single JS file? I guess one
would need a server side build step to "assemble" multiple Cell.js components
into a single one?

~~~
gliechtenstein
1) How does one cell send messages to another. I suppose this is left to the
application?

There are several ways to do this
[https://github.com/intercellular/tutorial#3-inter-
cellular-c...](https://github.com/intercellular/tutorial#3-inter-cellular-
communication)

I'm sure there are other ways of doing this in more creative ways, which was
my intention when I was designing this to be as simple and modular as
possible.

Another thing is, I say Cell has a decentralized architecture, but that
doesn't mean it only lets you build apps that way. The point is you can
construct these decentralized cells in any way you desire to build apps. So
you could build a perfectly centralized architecture with Cell as well.

2) How do you test code written in Cell.js? It appears to me that this is
going to be hard to do?

What I think is really cool about this approach is you are effectively
expressing an entire app as a piece of data
[https://github.com/intercellular/cell#3-app-as-
data](https://github.com/intercellular/cell#3-app-as-data) I think this
concept needs some time to sink it at first, but once you get it, it opens
door to a lot of creative ways of structuring your app.

For starters, any Javascript object can be composed/manipulated/constructed
with a function. And since you can express not just data but also the app
logic itself as data, you can do the same with stateless functions.

So YES, you can unit test your Cell apps. In fact it's easier than any other
existing frameworks since the entire app is built with functional programming
(no stateful class objects). You can write a whole bunch of functions to
construct/manipulate these "Genotype" objects and unit test those functions to
make sure they behave correctly. Hope this makes sense.

3) Is it intended that the entire webapp code be a single JS file? I guess one
would need a server side build step to "assemble" multiple Cell.js components
into a single one?

Nope, the only reason I packaged them as a single file at
[https://play.celljs.org](https://play.celljs.org) was just to make it easier
to understand as a demo (because splitting out into multiple files makes you
jump back and forth and is not ideal when you're just trying to have a quick
overview of the app structure).

But the beauty of this approach is, at the end of the day, all you need is a
JSON-like object that defines the looks and behaviors of your HTML nodes
throughout the DOM tree. Which means in real life, you would be splitting
these out into as many functions as you want (In fact this is one of the
strongest selling points of Cell. you can create as many of these functional
components as you want without overhead compared to other class based
frameworks) Really at the end of the day all you need is a function that
generates the Javascript object structure you need, which means you can
map/filter/reduce anything from Model to View to Controller logic.

Here's an example I was working on yesterday
[https://github.com/intercellular/jsonschema](https://github.com/intercellular/jsonschema)
It's a JSON schema validator, still work in progress since I just started it
yesterday but you get the point.

Hope this was helpful. Please feel free to ask more questions!

~~~
hannofcart
Thanks for taking the time to respond.

From everything you've said, most of what cell.js does is implement yet
another templating engine.

I think you said as much in your reply: "Really at the end of the day all you
need is a function that generates the Javascript object structure you need,
which means you can map/filter/reduce anything from Model to View to
Controller logic."

The additional work it does is injecting member variables for each of the cell
components (i.e. the "_" params).

Am I missing something?

------
meekaaku
This is interesting. I have had a look at the examples. One question that
comes to mind, how do you make a reusable component? Say I have: Part = {
$cell: true, $type: "h2", $text: "Part No: ", } Part is to be instantiated
multiple times in the same page, but I want different $text. How do I pass
this data?

~~~
tmzt
It seems to use functions for that purpose, look at the example with map.

I really like the linked to Android/iOS implementation of this concept.

------
shakna
JS frameworks and SEO have a rough history. Pre-rendering tends to be the
workaround, but Cell's lack of hierarchy might make that more difficult than
normal.

How would you work around it?

~~~
gliechtenstein
Cell itself does not have a structure. But the point is that you can easily
create a structure by composing cells. The DOM tree itself is a structure, and
Cell is designed to let developers take advantage of that instead of creating
a virtual DOM or stuff like that (without side effects).

Put it another way, you can create a fully centralized architecture using
Cell. One way of doing it is taking advantage of Cell's context inheritance
feature [https://github.com/intercellular/tutorial#b-context-
inherita...](https://github.com/intercellular/tutorial#b-context-inheritance-
and-polymorphism) but there can be many different ways. The whole point of
Cell is to exist as a minimal decentralized framework which you can utilize to
do things your way.

The benefit is you have the ability to create a completely decentralized DOM
tree, as well as create a centralized one.

As for the SEO issue, I have two answers:

1\. Pre-rendering on Cell is just a matter of transforming JSON into HTML (but
instead of using something like JSDOM on to create an entire DOM on the
server-side, you could just directly map the JSON into HTML. I actually have a
piece of code that does that as well but haven't released it yet.

2\. Handling pre-render is simpler than other frameworks because Cell can
seamlessly plug into an existing DOM tree
[https://github.com/intercellular/cell#b-plug-into-
existing-w...](https://github.com/intercellular/cell#b-plug-into-existing-
websites-like-a-widget) (it's actually one of the selling points of Cell
because no other framework makes this easy. If you have a static website and
just want to integrate a dynamic widget, there's no simple way to do so using
most of the popular frameworks because all of them basically take over the
entire frontend and you have to switch to that framework entirely just to add
that widget, or you need to use webpack, browserify, etc. to package them up
just so you can use it on the frontend as a bundle.js. This is also not the
most user-friendly way. Cell makes this super easy because there is no
dependency and no code packing process)

Hope this answers your question!

~~~
satyrnein
Let's say you pre-render the DOM nodes on the server. At that point, there's
no need to create them again in the browser from the JSON object. How would
you augment them to become Cells?

