Skip to content

Component

The component is a technique to decompose the large system into smaller, manageable, and reusable pieces. The component is the basic building block. Usually, a component is an autonomous and reusable module that encapsulates a set of data and functions.

An AppRun component is a mini-application with elm architecture, which means inside a component, there are state, view, and update. In addition, components provide a local scope.

Render the Component

To use the components, you can render them into an element.

const element = document.getElementById('my-app');
app.render(element, <Counter />);
When rendering the component, AppRun creates a component instance and renders it to the element.

Mount and Start

Or you can create the component using the constructor and mount the component instance to an element or to an element ID. When the component is mounted to an element ID, It will render the element only when it exists.

const element = document.getElementById('my-app');
new Counter().mount(element);

You can also pass the initial state into the component's constructor directly:

new Counter(100).mount(element);

When the component is mounted, by default, it won't display until the events come. It is useful in the single-page application (SPA) scenario where you can mount all components at once. Each component is activated by the routing events.

If you need the component to display the initial state, you can use the start function.

new Counter().start(document.body); // mount and display

You can render, mount, or start the component to document.body.

//
app.render(document.body, <Counter />);
//
new Counter().mount(document.body);
//
new Counter().start(document.body);

Child Component

Components can have child components.

class Child extends Component {
  state = {}
  view = state => <div></div>
  update = {}
}

class Parent extends Component {
  state = {}
  view = state => <div>
    <Child />
  </div>
  update = {}
}

You are not forced into the nested component structure. Sometimes, mounting components are more flexible. Please read this post, Redux vs. The React Context API vs. AppRun.

Component Events

A Component provides a local scope for events. The update registers the local events in the component. The this.run function fires local events that can only be picked up inside the component.

You can prefix the event name with #, / or @ to make it global.

class Counter extends Component {
   update = {
      '+1': state=>state+1, // local event
      '#+1': state=>state+1, // global event
   }
}

The app.run fires the global events that can be picked up by all components.

In addition, to use the update for defining event handlers, you can also use the @on decorator or the $on directive.

Event Handler Decorator

In the component class, we can use TypeScript to compile the @on decorators to create the event handlers without using the update object.

import app, { Component, on } from 'apprun';
class Counter extends Component {
  state = 0;
  view = state => <>
    <h1>{state}</h1>
    <button onclick={()=>this.run('-1')}>-1</button>
    <button onclick={()=>this.run('+1')}>+1</button>
  </>;

  @on('-1')
  decrease = state => state - 1;

  @on('+1')
  increase = state => state + 1;
}

Event Directive

We can also use the directive to simplify event handling.

import {app, Component} from 'apprun';
class Counter extends Component {
  state = 0;
  view = state => <div>
    <h1>{state}</h1>
    <button $onclick={state=>state-1}>-1</button>
    <button $onclick={state=>state+1}>+1</button>
  </div>;
}

Life Cycle Functions

Life Cycle Functions are callback functions that AppRun calls during the component life cycle. They are mounted, rendered, and unload.

import { app, Component } from 'apprun';

class MyApp extends Component {
  state = {};
  view = state => <div></div>;
  update = {};

  //life cycle functions
  mounted = (props, children, state) => state;
  rendered = state => {};
  unload = state => {};
}

app.render(document.body, <MyApp />);

mounted

The mounted function is called after the component instance is mounted to a DOM element. The mounted function can be used to set the initialize state.

mounted: (props: any, children: any[], state: T) => T | void;

Note: the mounted function is only called in the child component.

class Child extends Component {
  state = {} // you can define the initial state
  view = state => <div></div>
  update = {}
  mounted = (props, children) => { ...state, ...props } // this will be called, you can merge props into the state
}

class Parent extends Component {
  state = {} // you can define the initial state
  view = state => <div>
    <Child />
  </div>
  update = {}
  mounted = () => { } // this will NOT be called when component is created using the constructor
}
new Parent().start(document.body);

rendered

The rendered function is called after AppRun renders the result of the view function. The rendered function can be used to modify the DOM element using 3rd party libraries.

rendered: (state: T, props?: any[]) => void;

unload

The _unload function is called when the DOM element that the component is mounted to is removed or reused by other components. For example, the _unload function can be used to clean the resources created by the 3rd party libraries.

unload: (state: T) => void;

You can see, the component life cycle functions are useful for integrating 3rd party libraries.

Web Components

You can convert AppRun components into web components/custom elements. AppRun components become the custom elements that also can handle AppRun events.

<html>
<head>
  <meta charset="utf-8">
  <title>Counter as web component</title>
</head>
<body>
  <my-app id='counter'></my-app>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/custom-elements/1.1.2/custom-elements.min.js"></script>
  <script src="https://unpkg.com/apprun/dist/apprun-html.js"></script>
  <script>
    class Counter extends Component {
      constructor() {
        super();
        this.state = 0;
        this.view = state => `<div>
          <h1>${state}</h1>
          <button onclick='counter.run("-1")'>-1</button>
          <button onclick='counter.run("+1")'>+1</button>
          </div>`;
        this.update = {
          '+1': state => state + 1,
          '-1': state => state - 1
        };
      }
    }
    app.webComponent('my-app', Counter);
  </script>
</body>
</html>

HTML Child Components

Unlike JSX, you can embed component classes into JSX; when using HTML string components, you will need to make a web component/custom element. Then you can embed the components.

import app from 'apprun';
import MyComponent from './MyComponent';

app.webComponent('my-component', MyComponent);

const view = state => {
  return `<div>
    <my-component />
  </div>`;
};
app.start('my-app', state, view);