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.
It's definitely a framework though (note that this is not an inherently bad thing)
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 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
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?
Here's why I started working on Cell: Aside from Cell, I am working on a project called Jasonette 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... 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.
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?
There are several ways to do this https://github.com/intercellular/tutorial#3-inter-cellular-c...
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 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 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 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!
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?
I really like the linked to Android/iOS implementation of this concept.
ItemMaker = function(data){
return {
$cell: true,
$type: "h1",
style: "font-size: 100px;",
$text: data,
}
}
var i1 = ItemMaker('foo');
var i2 = ItemMaker('bar')How would you work around it?
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... 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... (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!