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 />);
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 runs after the component instance is mounted. It receives the
component's props, children, and the current state, and may return a new state —
which makes it a convenient place to merge incoming props into the state.
mounted: (props: any, children: any[], state: T) => T | void;
It is called:
- when you
start()a component (with emptypropsandchildren), and - when the component is rendered as a child component in JSX (with its actual
propsandchildren).
class Child extends Component {
state = {} // you can define the initial state
view = state => <div>{state.name}</div>
update = {}
// merge the props passed from the parent into the state
mounted = (props, children, state) => ({ ...state, ...props })
}
class Parent extends Component {
state = {}
view = state => <div>
<Child name="AppRun" />
</div>
update = {}
}
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.
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);