You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hyperstack Components are Ruby classes that inherit from the `HyperComponent` base class:
17
+
18
+
```ruby
19
+
classMyComponent < HyperComponent
20
+
...
21
+
end
22
+
```
23
+
> **[More on the HyperComponent base class](notes.html#the-hypercomponent-base-class)**
24
+
25
+
## The `render` Callback
26
+
27
+
At a minimum every *concrete* component class must define a `render` block which generates one or more child elements. Those children may in turn have an arbitrarily deep structure. **[More on concrete and abstract components](notes.html#abstract-and-concrete-components)**
28
+
29
+
```ruby
30
+
classComponent < HyperComponent
31
+
render do
32
+
DIV { } # render an empty div
33
+
end
34
+
end
35
+
```
36
+
> The code between the `do` and `end` is called a block. **[More here...](notes.html#blocks-in-ruby)**
37
+
38
+
To save a little typing you can also specify the top level element to be rendered:
39
+
40
+
```ruby
41
+
classComponent < HyperComponent
42
+
render(DIV, class: 'my-special-class') do
43
+
# everything will be rendered in a div
44
+
end
45
+
end
46
+
```
47
+
48
+
To render a component, you reference its class name as a method call from another component. This creates a new instance, passes any parameters and proceeds with the component lifecycle.
49
+
50
+
```ruby
51
+
classFirstComponent < HyperComponent
52
+
render do
53
+
NextComponent() # ruby syntax requires either () or {} following the class name
54
+
end
55
+
end
56
+
```
57
+
58
+
Note that you should never redefine the `new` or `initialize` methods, or call them directly. The equivalent of `initialize` is the `before_mount` method.
59
+
60
+
> The one exception to using `new` is within a spec to create a "headless" component in order to access its internal state and methods.
61
+
62
+
### Invoking Components
63
+
64
+
> Note: when invoking a component **you must have** a \(possibly empty\) parameter list or \(possibly empty\) block.
65
+
> ```ruby
66
+
MyCustomComponent() # ok
67
+
MyCustomComponent {} # ok
68
+
MyCustomComponent# <--- breaks
69
+
>```
70
+
71
+
## Multiple Components
72
+
73
+
So far, we've looked at how to write a single component to display data. Next let's examine how components are combined to build an application.
74
+
75
+
By building modular components that reuse other components with well-defined interfaces you can _separate the different concerns_ of your app. By building a custom component library for your application, you are expressing your UI in a way that best fits your domain.
76
+
77
+
### Composition Example
78
+
79
+
Let's create a simple Avatar component which shows a profile picture and username using the Facebook Graph API.
80
+
81
+
```ruby
82
+
classAvatar < HyperComponent
83
+
param :user_name
84
+
85
+
render(DIV) do
86
+
# for each param a method with the same name is defined
87
+
ProfilePic(user_name: user_name)
88
+
ProfileLink(user_name: user_name)
89
+
end
90
+
end
91
+
92
+
classProfilePic < HyperComponent
93
+
param :user_name
94
+
95
+
# note that in Ruby blocks can use do...end or { ... }
By convention your Hyperstack Components will inherit from the `HyperComponent` class, which typically would look like this:
17
-
18
-
```ruby
19
-
classHyperComponent
20
-
# All component classes must include Hyperstack::Component
21
-
includeHyperstack::Component
22
-
# The Observable module adds state handling
23
-
includeHyperstack::State::Observable
24
-
# The following turns on the new style param accessor
25
-
# i.e. param :foo is accessed by the foo method
26
-
param_accessor_style :accessors
27
-
end
28
-
29
-
# and is used like this:
30
-
31
-
classAnotherComponent < HyperComponent
32
-
...
33
-
end
34
-
```
35
-
36
-
Having an Application wide HyperComponent class allows you to modify component behavior on an application basis, similar to the way Rails uses `ApplicationRecord` and `ApplicationController` classes.
37
-
38
-
## The `render` Callback
39
-
40
-
At a minimum every component class must define a `render` block which returns one or more child elements. Those children may in turn have an arbitrarily deep structure.
41
-
42
-
```ruby
43
-
classComponent < HyperComponent
44
-
render do
45
-
DIV { } # render an empty div
46
-
end
47
-
end
48
-
```
49
-
50
-
To save a little typing you can also specify the top level element to be rendered:
51
-
52
-
```ruby
53
-
classComponent < HyperComponent
54
-
render(DIV, class: 'my-special-class') do
55
-
# everything will be rendered in a div
56
-
end
57
-
end
58
-
```
59
-
60
-
To render a component, you reference its class name as a method call from another component. This creates a new instance, passes any parameters and proceeds with the component lifecycle.
61
-
62
-
```ruby
63
-
classFirstComponent < HyperComponent
64
-
render do
65
-
NextComponent() # ruby syntax requires either () or {} following the class name
66
-
end
67
-
end
68
-
```
69
-
70
-
Note that you should never redefine the `new` or `initialize` methods, or call them directly. The equivalent of `initialize` is the `before_mount` method.
71
-
72
-
> The one exception to using `new` is within a spec to create a "headless" component in order to access its internal state and methods.
73
-
74
-
### Invoking Components
75
-
76
-
> Note: when invoking a component **you must have** a \(possibly empty\) parameter list or \(possibly empty\) block.
77
-
> ```ruby
78
-
MyCustomComponent() # ok
79
-
MyCustomComponent {} # ok
80
-
MyCustomComponent# <--- breaks
81
-
>```
82
-
83
-
## Multiple Components
84
-
85
-
So far, we've looked at how to write a single component to display data. Next let's examine one of React's finest features: composability.
86
-
87
-
### Motivation: Separation of Concerns
88
-
89
-
By building modular components that reuse other components with well-defined interfaces, you get much of the same benefits that you get by using functions or classes. Specifically you can _separate the different concerns_ of your app however you please simply by building new components. By building a custom component library for your application, you are expressing your UI in a way that best fits your domain.
3
+
### Ownership
90
4
91
-
### Composition Example
5
+
In the following example (copied from the previous section) instances of `Avatar`_own_ instances of `ProfilePic` and `ProfileLink`. In Hyperstack (like React), **an owner is the component that sets the `params` of other components**. More formally, if a component `X` is created in component `Y`'s `render` method, it is said that `X` is _owned by_`Y`. As will be discussed later a component cannot mutate its `params` — they are always consistent with what its owner sets them to. This fundamental invariant leads to UIs that are guaranteed to be consistent.
92
6
93
-
Let's create a simple Avatar component which shows a profile picture and username using the Facebook Graph API.
7
+
It's important to draw a distinction between the owner-owned-by relationship and the parent-child relationship. The owner-owned-by relationship is specific to Hyperstack/React, while the parent-child relationship is simply the one you know and love from the DOM. In the example above, `Avatar` owns the `DIV`, `ProfilePic` and `ProfileLink` instances, and `DIV` is the **parent**\(but not owner\) of the `ProfilePic` and `ProfileLink` instances.
94
8
95
9
```ruby
96
10
classAvatar < HyperComponent
97
11
param :user_name
98
12
99
-
render(DIV) do
100
-
# for each param a method with the same name is defined
101
-
ProfilePic(user_name: user_name)
102
-
ProfileLink(user_name: user_name)
13
+
render do# this can be shortened to render(DIV) do - see the previous section
14
+
DIVdo
15
+
ProfilePic(user_name: user_name) # belongs to Avatar, owned by DIV
16
+
ProfileLink(user_name: user_name) # belongs to Avatar, owned by DIV
@@ -121,38 +34,42 @@ class ProfileLink < HyperComponent
121
34
end
122
35
```
123
36
124
-
### Ownership
125
-
126
-
In the above example, instances of `Avatar`_own_ instances of `ProfilePic` and `ProfileLink`. In React, **an owner is the component that sets the `params` of other components**. More formally, if a component `X` is created in component `Y`'s `render` method, it is said that `X` is _owned by_`Y`. As discussed earlier, a component cannot mutate its `params` — they are always consistent with what its owner sets them to. This fundamental invariant leads to UIs that are guaranteed to be consistent.
127
-
128
-
It's important to draw a distinction between the owner-ownee relationship and the parent-child relationship. The owner-ownee relationship is specific to React, while the parent-child relationship is simply the one you know and love from the DOM. In the example above, `Avatar` owns the `div`, `ProfilePic` and `ProfileLink` instances, and `div` is the **parent**\(but not owner\) of the `ProfilePic` and `ProfileLink` instances.
129
-
130
37
### Children
131
38
132
-
When you create a React component instance, you can include additional React components or JavaScript expressions between the opening and closing tags like this:
39
+
Parent components may be provided a *block* (an anonymous Ruby function or callback) that the parent will use to generate its children. Inside
40
+
the block can be abitrary Ruby code that can generate any number of children *Elements* that will be rendered by the parent.
133
41
134
42
```ruby
135
-
Parent { Child() }
43
+
DIVdo
44
+
# this DIV will have two children
45
+
ProfilePic(user_name: user_name)
46
+
ProfileLink(user_name: user_name)
47
+
end
48
+
# or
49
+
A(href:"https://www.facebook.com/#{user_name}") do
50
+
# the anchor tag will have a single child the user_name string
51
+
user_name
52
+
end
136
53
```
137
54
138
-
`Parent` can iterate over its children by accessing its `children` method.
55
+
Note that as well as calling other Components (such as ProfilePic) the child block may return a string that will used as the single child content of
56
+
parent. If the child block has already generated a other components this final string will be ignored.
57
+
139
58
140
59
### Child Reconciliation
141
60
142
-
**Reconciliation is the process by which React updates the DOM with each new render pass.** In general, children are reconciled according to the order in which they are rendered. For example, suppose we have the following render method displaying a list of items. On each pass the items will be completely re-rendered:
61
+
**Reconciliation is the process by which the underlying React engine updates the DOM with each new render pass.** In general, children are reconciled according to the order in which they are rendered. For example, suppose we have the following render method displaying a list of items. On each pass the items will be regenerated and then merged into the DOM by React.
143
62
144
63
```ruby
145
64
param :items
146
65
render do
147
66
items.each do |item|
148
-
Pdo
149
-
item[:text]
150
-
end
67
+
P { item[:text] }
151
68
end
152
69
end
153
70
```
154
71
155
-
What if the first time items was `[{text: "foo"}, {text: "bar"}]`, and the second time items was `[{text: "bar"}]`? Intuitively, the paragraph `<p>foo</p>` was removed. Instead, React will reconcile the DOM by changing the text content of the first child and destroying the last child. React reconciles according to the _order_ of the children.
72
+
What if the first time items was `[{text: "foo"}, {text: "bar"}]`, and the second time items was `[{text: "bar"}]`? Intuitively, the paragraph `<p>foo</p>` was removed. Instead, React will reconcile the DOM by changing the text content of the first child and destroying the last child. React reconciles according to the _order_ of the children.
156
73
157
74
### Dynamic Children
158
75
@@ -171,8 +88,7 @@ The situation gets more complicated when the children are shuffled around \(as i
171
88
172
89
When React reconciles the keyed children, it will ensure that any child with `key` will be reordered \(instead of clobbered\) or destroyed \(instead of reused\).
173
90
174
-
The `key` should _always_ be supplied directly to the components in the array, not to the container HTML child of each component in the array:
175
-
91
+
> For best results the `key` is supplied at highest level possible.
176
92
```ruby
177
93
# WRONG!
178
94
classListItemWrapper < HyperComponent
@@ -193,7 +109,6 @@ class MyComponent < HyperComponent
193
109
end
194
110
end
195
111
```
196
-
197
112
```ruby
198
113
# CORRECT
199
114
classListItemWrapper < HyperComponent
@@ -215,7 +130,9 @@ class MyComponent < HyperComponent
215
130
end
216
131
```
217
132
218
-
> The `to_key` method: Any ruby object can be a key. Under the hood the HyperComponent DSL will call the object's `to_key` method which will respond with a unique value representing the object. For example if you pass an ActiveRecord model instance as a key, the result will be the database id of the model.
133
+
### The `to_key` method
134
+
135
+
Any ruby object can be a key. Under the hood Hyperstack will call the object's `to_key` method which will respond with an appropriate unique value representing the object. For example if you pass an ActiveRecord model instance as a key, the result will be the database id of the model.
0 commit comments