A Todo Application
A Todo Application
class TodoApp < React
def initialize
@items = []
@text = ''
end
def render
_h3 "TODO"
_TodoList items: @items
_form onSubmit: handleSubmit do
_label 'What needs to be done?', for: 'new-todo'
_input.new_todo! value: @text
_button "Add ##{@items.length + 1}"
end
end
def handleSubmit(e)
e.preventDefault()
return if @text.empty?
@items = @items.concat(text: @text, id: Date.now())
@text = ''
end
end
class TodoList < React
def render
_ul @@items do |item|
_li item.text, key: item.id
end
end
end
ReactDOM.render(
_TodoApp,
document.getElementById('todos-example')
)
Results
Commentary
Notable:
-
Again, The
initialize
method does not have to worry about setting upprops
or initializing thethis.state
property. More importantly, it does not have tobind
methods that are called as event handlers. All of this mindless administrivia is taken care of for you, allowing you to focus on your application logic, resulting in code that better expresses the developer’s intent. -
This
render
method uses a third method to define the HTML result, based on the approach the Wunderbar gem provides, which in turn was influenced by Jim Weirich’s Builder as well as Markaby. It differs from those libraries in the tags are prefixed by an underbar character, enabling tags to be unambiguously intermixed with control structures and other statements with a minimum of visual clutter.-
The outermost
div
element is not needed and omitted. It is only required in the JavaScript implementation of this method as the JSX used in therender
method can only return one value. The react filter will automatically detect such cases and wrap the set of elements in a React.fragment. -
HTML elements (like,
h3
) are expressed in lowercase and invoking other React components (likeTodoList
) is expressed in uppercase. -
If the element name is followed by a
.
, the next token is taken as aid
value if it is followed by an exclamation point, otherwise it is treated as a class name. This syntax was popularized by Markaby. Underscores in these names are converted to dashes. This is entirely optional, you can explicity codeid
andclass
attributes yourself. -
Arguments that are Strings become the element’s textContent, hashes that are passed as the last argument become the element’s attributes, and blocks are used to express nesting.
Taken together, the result is often much more compact and easier to read without all the angle brackets and curly braces. It also involves a lot less mental context switches to read as it is all Ruby.
-
-
In this example, there is no need to code a
handleChange
method at all, nor to bind it. The reason for this is the in React,input
elements are required to include anonChange
handler, so if one is not provided, the Ruby2JS react filter will provide one for you. Again, this is in the spirit of there being less boiler plate and allowing you to focus on the application logic. -
The
handleSubmit
method remains as it contains true application logic.-
Calling this method is done with
handleSubmit
, prefixing it with eitherthis.
orself.
is entirely optional. -
Calling JavaScript/DOM functions is done exactly as one would expect:
e.preventDefault()
. -
Testing for an empty string is done via the more direct
@text.empty?
rather than the more cumbersomethis.state.text.length === 0
. The functions filter assists with this conversion. -
As before, updating state is done via assignment statements to instance variables.
-
-
Moving on to the
TodoList
component, the call to_ul
takes advantage of the ability of blocks to not only express containment, but also to iterate over a list. This is done by passing an argument to the block.@@items
is the way theTodoList
component referencesthis.props.items
. This is not a direct mapping to Ruby semantics, but can be a convenient shorthand. If, for whatever reason, you don’t approve, the longer version is still available to you. -
In the
ReactDOM.render
call, the element to be rendered is expressed using wunderbar syntax.