@orby/core
v0.1.7
Published
Orby is a small experiment of functional components based on virtual-dom.
Downloads
39
Readme
Orby is a small and minimalist library to create modern interfaces based on JSX, Virtual-Dom and Functions.
Index
Motivation
Simplify the creation and maintenance of components, limiting its scope only functions, avoiding the use of classes, with the aim of simplifying the learning curve centered only on the use of functions, life cycle associated with nodes, hooks and Jsx.
Another motivation is the use of shadow-dom, as part of the process of detecting changes. Example of this is the creation of a component with style scope thanks to the use of shadow-dom, please see the following example and note the definition of the property scoped.
function Button(props){
return <div scoped>
<style>{`
:host{
padding : .5rem 1rem;
border:none;
background:black;
color:white
}
`}</style>
{props.children}
</div>
}
The <Button/>
component will be isolated in the Dom tree, this will define a closed scope of css styles.
JSX
JSX is defined as an extension of the JavaScript syntax, this allows to maintain a readable union between HTML and JS, and then be used for example in the manipulation of nodes, assignment of events, mutation of attributes and more.
When working with Orby, please consider the following differences with other libraries, such as React, in:
Without fragment support, Orby's components are more attached to the definition of a tree always maintaining a root node, this is how it is expressed in the Lifecycle.
Component
The functional Orbison components and you can manipulate the state of the nodes either through the Lifecycle associated with the virtual-dom or through the use of hooks.
Properties of the component
Like any functional component, the first argument of the component will always be its properties.
export function Button(props){
return <button onclick={props.click}>{props.children}</button>
}
The design pattern of purely functional components does not change if you were only limited to the use of Props.
Control of the context of the component
The context allows you to share a defined object at a higher level, it will be very useful if you look for interaction between 2 components.
export function Button(props,context){
return <button>{context.message}</button>
}
Another important point is that context management can be defined by using the context
property external to the component.
import {h,render} from "@orby/core";
import App from "./app";
render(
<App context={{message:"hi! Orby"}}/>,
document.querySelector("#app")
);
Lifecycle
The life cycle manifests itself on the virtual-dom in the creation, updating and elimination of the nodes, this is similar to how it operates in Hyperapp.
export function Button(){
return <button onCreate={handlerWithCreate}>Hi! Orby</button>
}
The DIFF process will invoke the onCreate
properties only when the <button/>
node is created in the dom tree. You can add the life cycle properties to the nodes you deem convenient.
onCreate
The onCreate
property is invoked when the node is added in the dom tree.
export function Button(){
return <button onCreate={(target:HTMLElement)=>{
/**algorithm**/
}}>Hi! Orby</button>
}
onCreated
The onCreated
property is invoked after the node was added to the dom tree and propagated the changes to its children.
export function Button(){
return <button onCreated={(target:HTMLElement)=>{
/**algorithm**/
}}>Hi! Orby</button>
}
onRemove
The onRemove
property is invoked when removing the node from the dom tree.
export function Button(){
return <button onRemove={(target:HTMLElement)=>{
/**algorithm**/
}}>Hi! Orby</button>
}
onRemoved
The onRemoved
property is invoked after removing the node from the dom tree and propagating the changes to its children.
export function Button(){
return <button onRemoved={(target:HTMLElement)=>{
/**algorithm**/
}}>Hi! Orby</button>
}
onUpdate
The onUpdate
property is invoked before propagating from the node of the dom tree. returnfalse
to avoid such propagation
export function Button(){
return <button onUpdate={(target:HTMLElement, prevProps:Object, nextProps:Object)=>{
/**algorithm**/
}}>Hi! Orby</button>
}
onUpdated
The onUpdated
property is invoked after propagating from the node of the dom tree.
export function Button(){
return <button onUpdated={(target:HTMLElement)=>{
/**algorithm**/
}}>Hi! Orby</button>
}
Hooks
Hooks are a powerful way to extend the behavior of a functional component created with Orby, this is a small implementation based on the React Hooks, consider also knowing the benefits of this pattern and rules associated with the use of Hooks
¿Why hooks?
Hooks are a powerful way to separate logic from the functional component, you can create custom effects that are linked to the component only with the invocation, it is such a link that these effects manage to control the state of the component without the need to know the same component.
useState
It allows using a state and associating it with the component, by default the components in Orby do not have status since they are functions, if you require a component that can manipulate changes based on a state you can use useState
within the component as many times as you deem appropriate. useState
append the status control only when it is invoked within the component
Unlike
useState
of React, this returns in the array a 3 argument, this one has the purpose of obtaining the state in asynchronous behaviors, its use is optional.
import {h,useState} from "@orby/core";
export function Button(){
let [state,useState] = useState();
}
Note that useState
returns an array, which you can use with Destructuring assignment to associate A variable, useState
also supports a first argument that defines the initial state.
import {h,useState} from "@orby/core";
export function Button(){
let [count,setCount] = useState(0);
}
if this first argument is a function, it is executed only when initializing the state of the component.
import {h,useState} from "@orby/core";
function createState(){
return {data:[]};
}
export function Button(){
let [state,useState] = useState(createState);
}
useEffect
It allows the execution of a function so many times the component is executed, this function is executed after the rendering process associated with patching the changes of the node.
It is easier to understand the execution of useEffect
by associating it with the life cycle methods of virtual-dom onCreated and onUpdated and onRemove.
import {h,useEffect} from "@orby/core";
export function Button(){
const [count, setCount] = useState(0);
useEffect(()=>{
document.title = `clicked ${count}`;
});
return <button onClick={()=>setCount(count+1)}>increment</button>;
}
If you try to assimilate the execution of the event onRemove of the virtual-dom within useEffect
, the function associated with useEffect
must return a function.
export function Button(props,context){
const [count, setCount] = useState(0);
useEffect(()=>{
document.title = `clicked ${count}`;
return ()=>{
document.title = `component remove`;
}
});
return <button onClick={()=>setCount(count+1)}>increment</button>;
}
useEffect
also receives a second argument, this gives the ability to limit the execution of the effect only to the changes associated with the second argument. The following example shows how to limit the execution of the effect only to a first instance.
export function Button(props,context){
useEffect(()=>{
console.log("component created")
return ()=>{
console.log("component remove")
}
},[true]);
return <button onClick={()=>setCount(count+1)}>increment</button>;
}
useReducer
small implementation use React
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'reset':
return {count: action.payload};
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
// A reducer must always return a valid state.
// Alternatively you can throw an error if an invalid action is dispatched.
return state;
}
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(
reducer,
initialState,
{type: 'reset', payload: initialCount},
);
return (
<div>
Count: {state.count}
<button
onClick={() => dispatch({type: 'reset', payload: initialCount})}>
Reset
</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
</div>
);
}
useContext
It allows to recover the context of the component, unlike React's useContext
, it returns the whole context if it does not have an argument.
const context = useContext(Context);
Context is the return of the createContext
instance, this homologous behavior of React.createContext
.
useMemo
allows to memorize the return of a callback, each time the second argument changes.
const list = useMemo(()=>{
let list =[];
for(let i=0;i<1000;i++){
list.push(i);
}
return list;
},[1000]);
this function is executed in second instance only if the values given in the second argument of useMemo are disintos to the previous one.
Special properties
key
It allows to define the identifier on the virtual-dom, to link it to a previous state, regardless of its order. The use of keys allows for example:
- Maintain an associative state of virtual-dom and a node indifferent to its order.
- Reduce the amount of manipulations associated with sun.
scoped
the scoped
property allows to enable the use of shadow-dom
on the node, when defining scoped as true, the DIFF process will understand that the nodes will be mounted in the shadowRoot
of the node.
export function Button(props){
return <div scoped>
<style>{`:host{background:crimson}`}</style>
{props.children}
</div>
}
context
The context
property allows you to add new properties to the context.
<ParentComponent context={{title:"Hi! Orby"}}>
<ChildComponent></ChildComponent>
</ParentComponent>
The example component ChildComponent
can make use of the context defined in a superior way. Note that it is not necessary to enter the component to create contexts.
createContext
This function allows you to create contexts that already reserve a namespace.
Default contexts in Orby
Orby by default allows to generate contexts in a simple way but this forces the child node to know the name of the property to access it and this can generate conflict.
import {h,render} from "@orby/core";
function Title(props,context){
return <h1>{context.myTitleContext}</h1>
}
render(
<Title context={{myTitleContext:"hello"}}/>,
document.body
)
context with createContext
Through createContext, you ensure that the name of the property is stored only within the createContext instance, reducing the possibility of name conflict.
import {h, createContext} from "@orby/core";
let Context = createContext({title:"hello"});
function Title(props,context){
return <h1>
<Context.Consumer>
{(data)=>data.title}
</Context.Consumer>
</h1>
}
render(
<Context.Provider>
<Title/>
</Context.Provider>,
document.body
)
consume context with useContext
By giving useContext the context instance this returns the value of the property associated with the reservation and name of the context
import {h,useContext,createContext} from "@orby/core";
let Context = createContext({title:"hello"});
function Title(props,context){
let data = useContext(Context);
return <h1>
{data.title}
</h1>
}
render(
<Context.Provider>
<Title/>
</Context.Provider>,
document.body
)
Examples
| Title | Description | link |
|---------|------------------------------|------------------------------------------------|
| Counter | shows the use of useState
| 🔗 link |
| Counter with Reducer | shows the use of useState
| 🔗 link |
| Hooks Router | show how to use useRouter
and useRedirect
| 🔗 link |
Utilities
| Title | Description | Repo | |---------|-----------------------------------------------------|------------------------------------------------| | Router | Manage your routes in a simple and declarative way | 🔗 link |