Note: this document is written intentionally close to ReactJS/Components and Props article to highlight differences and similarities with ReactJS.
Conceptually, components are script functions. They accept arbitrary inputs (called “props”) and return virtual elements describing what should appear on the screen.
The simplest way to define a component is to write a script function:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
This function is a valid Reactor component because it accepts a single "props" object argument with data and returns a virtual element. We call such components "function components" because they are literally script functions.
You can also use classes to define a component:
class Welcome : Element { function this(props) { this.props = props; } function render() { return <h1>Hello, {this.props.name}</h1>; } }
These two components will do exactly the same when used.
Class-based components have some additional features that we will discuss in the next sections. Until then, we will use function components for their conciseness.
Full declaration of component functions and constructors may look like as:
function FunctionComponent(props,kids,states) {}
Where:
:
to names). State flags (attributes) do not participate in reconciliation.Example, this declaration:
<FunctionComponent mode="start" :value="foo"> <div>bar</div> </FunctionComponent>
will get call of FunctionComponent() with this parameters:
{ mode: "start" }
[ <div>bar</div> ]
{ value: "foo" }
Constructors of class components follow the same convention with the only difference - additional parent parameter
class ClassComponent: Reactor.Component { function ClassComponent(props,kids,states,parent) {} }
The parent argument receives DOM element - future or existing container DOM element.
Previously, we only encountered Reactor virtual elements that represent DOM tags:
const velement = <div />;
However, elements can also represent user-defined components:
const velement = <Welcome name="John" />;
When Sciter script compiler sees an element representing a user-defined component, it passes its attributes to this component as a single object. We call this object “props”.
For example, this code renders “Hello, Ivan!” on the page:
function Welcome(props) { return <h1>Hello, {props.name}!</h1>; } const velement = <Welcome name="Ivan" />; $(body).content(velement);
Check sample: {sdk}/samples/+reactor/doc-samples/components/1-welcome.htm
Let us recap what happens in the example above:
element.content(velement)
with the <Welcome name="Ivan" />
element. Welcome
component with {name: "Ivan"}
as the props object.Welcome
component returns a <h1>Hello, Sara</h1>
virtual element as the result.Element.content()
function inserts DOM element <h1>Hello, Ivan!</h1>
using that virtual element definition.Components can refer to other components in their output. This lets us use the same component abstraction for any level of detail. A button, a form, a dialog, all those may commonly be expressed as components.
For example, we can create an App
component that renders Welcome
many times:
function Welcome(props) { return <h1>Hello, {props.name}!</h1>; } function App() { return <main> <Welcome name="Ivan" /> <Welcome name="Olga" /> <Welcome name="Andrew" /> </main>; } $(main).merge( <App /> );
Check sample: {sdk}/samples/+reactor/doc-samples/components/2-composing.htm
Don’t be afraid to split components into smaller components.
For example, consider this Comment
component:
function Comment(props) { return <div.comment> <div .userinfo> <img .avatar src={props.author.avatarUrl} alt={props.author.name} /> <div .userinfo-name>{props.author.name}</div> </div> <div .comment-text>{props.text}</div> <div .comment-date>{formatDate(props.date)}</div> </div>; }
Check sample: {sdk}/samples/+reactor/doc-samples/components/3-extracting.htm
It accepts author
(an object), text
(a string), and date
(a date) as props, and describes a comment in a chat implementation for example.
This component can be tricky to change and maintain because of all the nesting, and it is also hard to reuse individual parts of it.
Let’s extract a few components from it. First, we will extract Avatar
:
function Avatar(props) { return <img.avatar src={props.user.avatarUrl} alt={props.user.name}/>; }
Next, we will extract a UserInfo
component that renders an Avatar
next to the user’s name:
function UserInfo(props) { return <div.userinfo> <Avatar user={props.user} /> <div.userinfo-name>{props.user.name}</div> </div>; }
And finally simplified Comment
that is composed from the above:
function Comment(props) { return <div.comment> <UserInfo user={props.author} /> <div.comment-text>{props.text}</div> <div.comment-date>{formatDate(props.date)}</div> </div>; }
Extracting components might seem like grunt work at first, but having a palette of reusable components pays off in larger apps. A good rule of thumb is that if a part of your UI is used several times (Button
, Panel
, Avatar
), or is complex enough on its own (App
, FeedStory
, Comment
), it is a good candidate to be a reusable component.