Component Children, Keys and Fragments
Components often have child components. If you consider HTML tags like
DIV
, UL
, and TABLE
you will see you are already familiar with this concept:DIV(id: 1) do
SPAN(class: :span_1) { 'hi' }
SPAN(class: :span_2) { 'there' }
end
Here we have a
DIV
that receives one param, an id equal to 1 and has two child elements - the two spans.The
SPAN
s each have one param (its css class) and has one child element - a string to render.Hopefully at this point the DSL is intuitive to read, and you can see that this will generate the following HTML:
<div id=1>
<span class='first_span'>hi</span>
<span class='second_span'>there</span>
</div>
Children do not have to be statically generated. Let's sort a string of text into individual word counts and display it in a list:
# assume text is a string of text
UL do
word_count(text).each_with_index do |word, count|
LI { "#{count} - #{word}" }
end
end
In this case you can see that we don't determine the actual number or contents of the
LI
children until runtime.In the above example what would happen if the contents of
text
were dynamically changing? For example if it was associated with a text box that the user was typing into, and we updated text
whenever a word was entered.In this case as the user typed new words, the
word_count
would be updated and the list would change. However actually only the contents of one of the list items (LI
blocks) would actually change, and perhaps the sort order. We don't need to redraw the whole list, just the one list item that changed, and then perhaps shuffle two of the items. This is going to be much faster than redrawing the whole list.Like React, Hyperstack provides a special
key
param that can identify child elements so that the rendering engine will know that while the content and order may change on some children, it can easily identify the ones that are the same: LI(key: word) { "#{count} - #{word}"}
You don't have to stress out too much about keys, its easy to add them later. Just keep the concept in mind when you are generating long lists, tables, and divs with many children.
Application defined components can also receive and render children. A component's
children
method returns an enumerable that is used to access the unrendered children of a component. The children can then be rendered using the render
method which will merge any additional parameters and render the child.class Indenter < HyperComponent
render(DIV) do
IndentLine(by: 10) do # see IndentLine below
DIV {"Line 1"}
DIV {"Line 2"}
DIV {"Line 3"}
end
end
end
class IndentLine < HyperComponent
param by: 20, type: Integer
render(DIV) do
children.each_with_index do |child, i|
child.render(style: {"margin-left" => by*i})
end
end
end
A render block may generate multiple values. React assumes when a Component generates multiple items, the item order and quantity may change over time and so will give a warning unless each element has a key:
class ListItems < HyperComponent
render do
# without the keys you would get a warning
LI(key: 1) { 'item 1' }
LI(key: 2) { 'item 2' }
LI(key: 3) { 'item 3' }
end
end
# somewhere else:
UL do
ListItems()
end
If you are sure that the order and number of elements will not change over time you may wrap the items in the
FRAGMENT
pseudo component:class ListItems < HyperComponent
render(FRAGMENT) do
LI { 'item 1' }
LI { 'item 2' }
LI { 'item 3' }
end
end
Last modified 2yr ago