Components

Hyperloop user interfaces are composed of React Components written in Ruby.

Here is the basic structure of a Component:

classStrippedBackComponent<Hyperloop::Componentrender(DIV)doendend

As you can see, a Component is just a Ruby class which inherits from Hyperloop::Component. At a minimum, a Component must implement a render macro that returns just one HTML element. Under the covers, HyperReact uses Opal to compile this Component into JavaScript then hands it to React to mount as a regular JavaScript React Component.

As with React, there are no templates in Hyperloop, your user interface is made up of Components which mix conditional logic and HTML elements to build the user interface. Unlike React, where you code in JSX and JavaScript, Hyperloop lets you keep all your code in Ruby.

Let's add a little functionality to this Component - you can edit this code if you would like to experiment.

class SimpleComponent < Hyperloop::Component
render(DIV) do
BUTTON { 'Push the button' }.on(:click) do
alert 'You did it!'
end
end
end

There are a few things to notice in the code above.

Every Component must have a render macro which must return just one HTML element. The syntax of render(DIV) is a shorthand for this which will return one div.

HTML built-in elements (DIV, BUTTON, TABLE, etc) are in uppercase, we believe this reads better alongside Components which are in CamelCase and methods in snake_case

We added an event handler to the button. You can do this for any HTML element in the same way.

Rendering Components

Hyperloop's architecture encourages you to write simple Components that perform single tasks and render other Components.

Passing parameters

Data is passed downward from a parent Component to its children. There are various techniques for passing data upward and (better still) keeping data in Stores independently of Components but we will address that later.

The syntax for adding components is either MyComponent() or MyComponent {} but never just MyComponent. Sometimes you use both - BUTTON(class: 'my-class') { "Click Me" }. Everything in the brackets is passed to the Component as parameters and everything in the curly brace is rendered within the Component.

Parameters can be strongly typed param :name, type: String and considering this code will be compiled to JavaScript this is a good idea.

State and Conditional Execution

One of the greatest things about React is that it encourages you to write code in a declarative way with Components that manage their own state (or defer their state to Stores, but we will cover that later). As state changes, React works out how to render the user interface without you having to worry about the DOM - the user interface re-renders itself when it needs to.

The best way to think about this is to imagine your code constantly looping and the program execution changing as the state variables and conditional logic changes. This is pretty much what is going on under the covers, with React being clever about which parts of the UI need to change and be re-rendered.

JavaScript Libraries

JavaScript components are accessed directly from within your Ruby code!

It is important to emphasize that Hyperloop gives you full access to all JavaScript libraries and components from directly within your Ruby code. Everything you can do in JavaScript is simple to do in Ruby, this includes passing parameters between Ruby and JavaScript and even passing in Ruby lambdas as JavaScript callbacks.

There are a few ways of accomplishing this, one of which is demonstrated below. Here we wrap a JavaScript library ReactPlayer with a Ruby class Player so that it is accessible in our Ruby code.

You can also import JavaScript libraries using NPM/Yarn and Webpack/Webpacker and have them available to your Hyperloop Components. We have tutorials which will show you exactly how this works.

That concludes the introduction to Components. To learn more about Components please see the Tutorials and also the comprehensive Docs

In this section, we have shown you how Components work, how you can string them together to build a page, how they pass parameters to their children and even how you can access the complete universe of JavaScript libraries from right within your components.

Next, we are going to cover Stores which are a very clever way of separating our application State from Components so that many Components can share the same state. Using Stores make application design a lot cleaner as you do not need to worry abut passing parameters all over the place.