As the name suggest, this page deals with component lifecycle, which is an analogy that a component will be created at a certain point in time and will be removed for some reason too.
To understand component lifecycle is to basically do some work when a component is either created, updated or removed, and this work can be fetching data, adjusting data or simple clean-up function to remove unnecessary data when not in used.
Component Creation
We might need to fetch some data or do some operations beforehand while a component is being created, now there can be two scenario to this:
A very important function needs to run before the component is created, and we need a progress component
Some basic function like logs and so to be run
Let's say we need to fetch something, we can do so but fetch is asynchronous and as we know, a functional component can never be asynchronous, hence we have to use the onLoad() function to run the fetch operation, while we can fallback to a loading state.
Let's see some example:
function randomUser() {
const user = State({})
return div(
p(`Name: ${user.get().name}`),
p(`Age: ${user.get().age}`)
)
}
As of now, it will show undefined, since there are no name or age properties. Let's now fetch the data and assign it to our state, and we will use the onLoad() function for that
function randomUser() {
const user = State({})
onLoad(async () => {
let raw = await fetch("https://randomuser.me/api/") //the api to get random names
let json = JSON.parse( await raw.text() )
user.set({
name: json.results[0].name.first, //the API was structured this way, it's not implementation
age: json.results[0].dob.age
})
})
return $(() => div(
p(`Name: ${user.get().name}`),
p(`Age: ${user.get().age}`)
), user)
}
While this code does works, the problem with it is that first it says undefined, then after some time it shows the data. We might want to implement some sort of in-between loader, like displaying "Loading Data". Recall in reactivity, we used the When() function to create a condition? we can do here too!
Let's see that in action:
function randomUser() {
const user = State("")
onLoad(async () => {
let raw = await fetch("https://randomuser.me/api/") //the api to get random names
let json = JSON.parse( await raw.text() )
user.set({
name: json.results[0].name.first, //the API was structured this way, it's not implementation
age: json.results[0].dob.age
})
})
return $(() => When(user.get() != "",
div(
p(`Name: ${user.get().name}`),
p(`Age: ${user.get().age}`)
), p("Loading Data...")
), user)
}
DIY 1.0
From the above code, we might say that the fetch function fails. Handle that error, and also show a paragraph "Failed to load" if the fetch function fails.
Hint: When() function can be nested
The onLoad() function also takes a list of states just like the $() function, that means we can add dependent states too, that when updated, will invoke the callback passed into onLoad() function.
Let's implement a button that will update the user with new data
function randomUser() {
const user = State("")
onLoad(async () => {
let raw = await fetch("https://randomuser.me/api/") //the api to get random names
let json = JSON.parse( await raw.text() )
user.set({
name: json.results[0].name.first, //the API was structured this way, it's not implementation
age: json.results[0].dob.age
})
})
return $(() => When(user.get() != "",
div(
p(`Name: ${user.get().name}`),
p(`Age: ${user.get().age}`),
button("Update Data")
), p("Loading Data...")
), user)
}
Now let's add a new state in which we will store the string "update" whenever we will need to update
function randomUser() {
const user = State("")
const reload = State()
onLoad(async () => {
let raw = await fetch("https://randomuser.me/api/") //the api to get random names
let json = JSON.parse( await raw.text() )
user.set({
name: json.results[0].name.first, //the API was structured this way, it's not implementation
age: json.results[0].dob.age
})
})
return $(() => When(user.get() != "",
div(
p(`Name: ${user.get().name}`),
p(`Age: ${user.get().age}`),
button("Update Data")
), p("Loading Data...")
), user)
}
Now we will add a click event to the button and update the state of the reload variable
function randomUser() {
const user = State("")
const reload = State()
onLoad(async () => {
let raw = await fetch("https://randomuser.me/api/") //the api to get random names
let json = JSON.parse( await raw.text() )
user.set({
name: json.results[0].name.first, //the API was structured this way, it's not implementation
age: json.results[0].dob.age
})
})
return $(() => When(user.get() != "",
div(
p(`Name: ${user.get().name}`),
p(`Age: ${user.get().age}`),
button("Update Data").event("click", () => {
reload.set("update")
}),
), p("Loading Data...")
), user)
}
Now to trigger the update, we need to bind the value of reload state to the onLoad() function
function randomUser() {
const user = State("")
const reload = State()
onLoad(async () => {
let raw = await fetch("https://randomuser.me/api/") //the api to get random names
let json = JSON.parse( await raw.text() )
user.set({
name: json.results[0].name.first, //the API was structured this way, it's not implementation
age: json.results[0].dob.age
})
}, reload) //binded
return $(() => When(user.get() != "",
div(
p(`Name: ${user.get().name}`),
p(`Age: ${user.get().age}`),
button("Update Data").event("click", () => {
reload.set("update"),
user.set("") //to make sure it shows loading
})
), p("Loading Data...")
), user)
}
Note: The above code is not for update lifecycle, but rather a feature of onLoad() that can also take update lifecycle. onLoad function will run atleast once when the component is created, but an update function must run only when a state is updated, but not both times
Component Update
A component in weblabs is updated only when the state that it is bind to is updated. The State() function provides an interface onUpdate() where we can attach arbitary function which will be called one-after-one when the state is update.
We will use the above example code, and will add an update event to the reload state which when updated will alert the user that the data is being updated
function randomUser() {
const user = State("")
const reload = State()
reload.onUpdate(() => {
alert("Data will be updated")
})
onLoad(async () => {
let raw = await fetch("https://randomuser.me/api/") //the api to get random names
let json = JSON.parse( await raw.text() )
user.set({
name: json.results[0].name.first, //the API was structured this way, it's not implementation
age: json.results[0].dob.age
})
}, reload)
return $(() => When(user.get() != "",
div(
p(`Name: ${user.get().name}`),
p(`Age: ${user.get().age}`),
button("Update Data").event("click", () => {
reload.set("update")
user.set("") //make sure it shows Loading...
}),
), p("Loading Data...")
), user)
}
Component Removal
A Component might get removed from the DOM depending upon situations such as:
A conditional rendering
A Iterative rendering
Other external factors
If the object itself was removed, be it from outside or using functions from weblabs, framework is still smart enough to detect exactly which one was removed.
Component Removal lifecycle can be applied to all sorts of components, be it variable, stateless, functional and what not, with only one requirement for the component itself should be a HTMLElement.
Let's try using this.
From the above code, we know that the p("Loading data...") will be removed once the data is loaded, so we can detect this and alert the user that the data loading has been finished.
To do so, we will use yet another function onRemove(), which takes the component we wish to detect when removed, and also a callback which is fired when the component has been removed
function randomUser() {
const user = State("")
const reload = State()
reload.onUpdate(() => {
alert("Data will be updated")
})
onLoad(async () => {
let raw = await fetch("https://randomuser.me/api/") //the api to get random names
let json = JSON.parse( await raw.text() )
user.set({
name: json.results[0].name.first, //the API was structured this way, it's not implementation
age: json.results[0].dob.age
})
}, reload)
return $(() => When(user.get() != "",
div(
p(`Name: ${user.get().name}`),
p(`Age: ${user.get().age}`),
button("Update Data").event("click", () => {
reload.set("update")
user.set("") //make sure it shows Loading...
})
), p("Loading Data...").onRemove(() => {
alert("Data finish loading")
})
), user)
}
Congrats! you now know everything there is to learn about weblabs, and honestly nothing left to learn. The next chapter Routing is an additional tool that weblabs provides just for the sake of simplicity and ease of use, since anyone can implement a router for a framework, like react-router for reactjs was not made by react ( Ironical I know )