From f15727a64401c5958f88539f1d50bfc9f6436f63 Mon Sep 17 00:00:00 2001 From: Rafael Weinstein Date: Fri, 20 Mar 2015 13:24:10 -0700 Subject: [PATCH] [Effen] Move README.md to correct directory TBR=abarth BUG= Review URL: https://codereview.chromium.org/1024183002 --- examples/fn/README.md | 135 ------------------------------------------ 1 file changed, 135 deletions(-) delete mode 100644 examples/fn/README.md diff --git a/examples/fn/README.md b/examples/fn/README.md deleted file mode 100644 index fab026ea72..0000000000 --- a/examples/fn/README.md +++ /dev/null @@ -1,135 +0,0 @@ -Effen (fn) -=== - -Effen is a prototype of a functional-reactive framework for sky which takes inspiration from [React](http://facebook.github.io/react/). The code as you see it here is a first-draft, is unreviewed, untested and will probably catch your house on fire. It is a proof of concept. - -Effen is comprised of three main parts: a virtual-dom and diffing engine, a component mechanism and a very early set of widgets for use in creating applications. - -The central idea is that you build your UI out of components. Components describe what their view should look like given their current configuration & state. The diffing engine ensures that the DOM looks how the component describes. - -If you just want to dive into code, see the `sky/examples/stocks-fn`. - -Is this the official framework for Sky? ---------------------------------------- -Nope, it's just an experiment. We're testing how well it works and how we like it. - -Hello World ------------ - -To build an application, create a subclass of App and instantiate it. - -```HTML - - -``` - -```JavaScript -// In helloworld.dart -import '../fn/lib/fn.dart'; - -class HelloWorldApp extends App { - Node build() { - return new Text('Hello, World!'); - } -} -``` -An app is comprised of (and is, itself, a) components. A component's main job is to implement `Node build()`. The idea here is that the `build` method describes the DOM of a component at any given point during its lifetime. In this case, our `HelloWorldApp`'s `build` method just returns a `Text` node which displays the obligatory line of text. - -Nodes ------ -A component's `build` method must return a single `Node` which *may* have children (and so on, forming a *subtree*). Effen comes with a few built-in nodes which mirror the built-in nodes/elements of sky: `Text`, `Anchor` (``, `Image` (``) and `Container` (`
`). `build` can return a tree of Nodes comprised of any of these nodes and plus any other imported object which extends `Component`. - -How to structure you app ------------------------- -If you're familiar with React, the basic idea is the same: Application data flows *down* from components which have data to components & nodes which they construct via construction parameters. Generally speaking, View-Model data (data which is derived from *model* data, but exists only because the view needs it), is computed during the course of `build` and is short-lived, being handed into nodes & components as configuration data. - -What does "data flowing down the tree" mean? --------------------------------------------- -Consider the case of a checkbox. (i.e. `widgets/checkbox.dart`). The `Checkbox` constructor looks like this: - -```JavaScript - ValueChanged onChanged; - bool checked; - - Checkbox({ Object key, this.onChanged, this.checked }) : super(key: key); -``` - -What this means is that the `Checkbox` component is *never* "owns" the state of the checkbox. It's current state is handed into the `checked` parameter, and when a click occurs, the checkbox invokes its `onChanged` callback with the value it thinks it should be changed to -- but it never directly changes the value itself. This is a bit odd at first look, but if you think about it: a control isn't very useful unless it gets its value out to someone and if you think about databinding, the same thing happens: databinding basically tells a control to *treat some remote variable as its storage*. That's all that is happening here. In this case, some owning component probably has a set of values which describe a form. - -Stateful vs. Stateless components ---------------------------------- -All components have access to two kinds of state: (1) configuration data (constructor arguments) and (2) private data (data they mutate themselves). While react components have explicit property bags for these two kinds of state (`this.prop` and `this.state`), Effen maps these ideas to the public and private fields of the component. Constructor arguments should (by convention) be reflected as public fields of the component and state should only be set on private (with a leading underbar `_`) fields. - -All (non-component) Effen nodes are stateless. Some components will be stateful. This state will likely encapsulate transient states of the UI, such as scroll position, animation state, uncommitted form values, etc... - -A component can become stateful in two ways: (1) by passing `super(stateful: true)` to its call to the superclass's constructor, or by calling `setState(Function fn)`. The former is a way to have a component start its life stateful, and the latter results in the component becoming statefull *as well as* scheduling the component to re-build at the end of the current animation frame. - -What does it mean to be stateful? It means that the diffing mechanism retains the specific *instance* of the component as long as the component which builds it continues to require its presence. The component which constructed it may have provided new configuration in form of different values for the constructor parameters, but these values (public fields) will be copied (using reflection) onto the retained instance whose privates fields are left unmodified. - -Rendering ---------- -At the end of each animation frame, all components (including the root `App`) which have `setState` on themselves will be rebuilt and the resulting changes will be minimally applied to the DOM. Note that components of lower "order" (those near the root of the tree) will build first because their building may require rebuilding of higher order (those near the leaves), thus avoiding the possibility that a component which is dirty build more than once during a single cycle. - -Keys ----- -In order to efficiently apply changes to the DOM and to ensure that stateful components are correctly identified, Effen requires that `no two nodes (except Text) or components of the same type may exist as children of another element without being distinguished by unique keys`. [`Text` is excused from this rule]. In many cases, nodes don't require a key because there is only one type amongst its siblings -- but if there is more one, you must assign each a key. This is why most nodes will take `({ Object key })` as an optional constructor parameter. In development mode (i.e. when sky is built `Debug`) Effen will throw an error if you forget to do this. - -Event Handling --------------- -To handle an event is to receive a callback. All elements, (e.g. `Container`, `Anchor`, and `Image`) have optional named constructor arguments named `on*` whose type is function that takes a single `sky.Event` as a parameter. To handle an event, implement a callback on your component and pass it to the appropriate node. If you need to expose the event callback to an owner component, just pipe it through your constructor arguments: - -```JavaScript -class MyComp extends Component { - MyComp({ - Object key, - sky.EventListener onClick // delegated handler - }) : super(key: key); - - Node build() { - return new Container( - onClick: onClick, - onScrollStart: _handleScroll // direct handler - ); - } - - _handleScroll(sky.Event e) { - setState(() { - // update the scroll position - }); - } -} -``` - -*Note: Only a subset of the events defined in sky are currently exposed on Element. If you need one which isn't present, feel free to post a patch which adds it.* - -Styling -------- -Styling is the part of Effen which is least designed and is likely to change. At the moment, there are two ways to apply style to an element: (1) by handing a `Style` object to the `style` constructor parameter, or by passing a `String` to the `inlineStyle` constructor parameter. Both take a string of CSS, but the construction of a `Style` object presently causes a new `