---
description: Make your app truly interactive
---

# Reactivity

Till now, we saw how we can build UI using Weblabs, but it was not dynamic, and it will not change if the user interacts. So, we need to do something to make sure the UI itself reacts to changes, like updating of state.

## States

A state is a variable that can store values, and will also update the UI if the value was changed.

Weblabs has a built-in State API, the State() function, which will help in creating the state for an UI. The state function takes in one variable, that is the initial or starting state of the UI, then the state returns an object with three methods:

* `set(value)`: Sets a new state
* `get()`: Gets the current state
* `subscribe(...)` : Subscribes to the three action events of the states, namely
  * -> `set` : Calls the callback function when a value is requested to be stored on the state. The callback can modify or disallow the value from being stored.
  * -> `get` : Calls the callback function when a value is requested to be retrieved from the state
  * -> `set:after` : Calls the callback function after the set operation has been finished. This event can take multiple callbacks, and are primarily used for component re-rendering
  * -> `set:only` : calls the callback function only when the value is not mutated in the state when a new value is set; i.e the value is not mutated in the `set` event
* `registerEvent<type>(eventName: string, callback: (data: type) => void)` : Sets up a custom event that takes a callback, which will be called when the custom event is emitted. The callback can mutate value inside the state, recieve it and do any operation required.
* `emitEvent<type>(eventName: string, data: type)` : calls the specific event, if registered. Else it will show error, and if used alogside `DebugRender()` will show error on screen.

Weblabs has selective state updates, that means if the state was updated, it will not update anything unless we bind the state to the component we want to update when the state changes, and here comes the `$()` function.

This is a JQuery style call that reminds you that it is sensitive to state updates.

Too complicated? Let's cover one thing after another.

Let's create a simple function as we would do when creating a new custom component

```javascript
function MyComponent() {
    return div(
        input()
    )
}

render("app", MyComponent())
```

Here we created a component that would show a text input. But this is not doing anything as such. We want to store the data the user types in the input box.

Now if I recall, we have an event called `input` that will be fired whenever the user types anything on the input box. Let's add that event then

```javascript
function MyComponent() {
    return div(
        input().event("input", e => console.log(e.target.value))
    )
}

render("app", MyComponent())
```

To check, we will render this component using the render function and open the developer tools > console. There you must see the values of the text box logged as you continue to type in it. That means we must store this value to a state.

To store the value, we will use state, to make sure our UI updates whenever the state is changing

```javascript
function MyComponent() {
    
    const userInput = State("") //empty state for now

    return div(
        input().event("input", e => userInput.set(e.target.value))
    )
}

render("app", MyComponent())
```

Now let's create a paragraph to show the text the user is typing

```javascript
function MyComponent() {
    
    const userInput = State("") //empty state for now

    return div(
        input().event("input", e => userInput.set(e.target.value))
        p(userInput.get())
    )
}

render("app", MyComponent())
```

But nothing is happening! If you recall, I said that Weblabs uses selective updates using Component function. So for the paragraph to update it's text, we need to tell Weblabs to update it, and we can do so by wrapping the paragraph function inside the $ function as a callback, and also binding the state that we want to re-render when updates

```javascript
function MyComponent() {
    
    const userInput = State("") //empty state for now

    return div(
        input().event("input", e => userInput.set(e.target.value))
        $(() => p(userInput.get()), userInput)
    )
}

render("app", MyComponent())
```

Now it must work, and the best part is only the p tag is getting re-rendered, and the entire app is unaffected by this. This selective updating based on states is useful to make sure your app does not do unnecessary updates.

The stateful function $ will return the same element as in it's callback, hence it can be used to add ids and classes and other mutations even outside the stateful function

There is no restriction in keeping the state outside the function, just make sure it is accessible. The state, regardless of how it was recieved will work flawlessly. States can be passed to other components as props as well, and then can be used to bind to a Component.

This can be daunting at first, but I have created some examples where you have to build complex apps, and there you will learn even more ways to use Weblabs and it's seemingly infinite variations.

## Conditional Rendering

Sometimes, just showing states is not enough, and you need to show the data based on some other criterias, and you may need to use conditional rendering.

There are a lot of ways you can do conditional rendering in Weblabs, some simple while some daunting, but we will cover the two of the most efficient ones which you can use.

To give a overview, let's take the app we made above, and create a condition that if the length of the input is greater than 5, then only we will show it, otherwise we will show "Not Big Enough"

### Using self-invoking function

We can declare a function and call it on the go right there without really creating a function and storing it somewhere.

A self-invoking function looks somewhat like this:

```javascript
(() => {
    console.log("Hello World")
})() //self invoked
```

To learn more about self-invoking functions, visit: [https://developer.mozilla.org/en-US/docs/Glossary/IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE)

Now let's implement our app using this feature

```javascript
function MyComponent() {
    
    const userInput = State("") //empty state for now

    return div(
        input().event("input", (e) => userInput.set(e.target.value)),
        $(() => 
        (() => {
            if ( userInput.get().length > 5 ) {
                return p(userInput.get())
            } else {
                return p("Not Big Enough")
            }
        })(), userInput)
    )
}

render("app", MyComponent())
```

### Using Weblabs' way

The above code was really bad. Too hard to read and to get an idea of what is exactly going on. Weblabs' way is a lot more readable and better than the above example.

The `When()` function is used to achieve the same result, except without all the hassle as above. The same code can be re-written as such:

```javascript
function MyComponent() {
    
    const userInput = State("") //empty state for now

    return div(
        input().event("input", (e) => userInput.set(e.target.value)),
        $(() => 
        When(userInput.get().length > 5, 
            p(userInput.get()),
            p("Not Big Enough")
        ), userInput)
    )
}

render("app", MyComponent())
```

The `When()` function takes three input, the first argument is the condition, the second argument is the value if the condition was true, and the third value if the condition was false.

`When()` function is nestable with other Whens, components, and even HOCs like ids.

## Loops

Sometimes, you need create some component that could dynamically load data from an array or object, so you might loop the component to display each element properly.

### Looping over arrays

Looping over arrays means that we loop over a given array, use each element and create a component for that specific element, and then return the element. We will use the built-in javascript methods:

* [array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
* [array.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)

Now, I am not going into details, so I have linked their docs, but we know that these functions will return an array, but to be used as component, we need to have component, hence we will use the spread operator.

Let's see some example:

```javascript
//say I have the following data
var users = [
    {
        name: "Mayukh",
        age: 17
    },
    {
        name: "Weblabs",
        age: 0
    }
]
```

Now let's say I have a component that takes name and age and shows us them in the view:

```javascript
function Users(name, age) {
    return div(
        p(`Name: ${name}`),
        p(`Age: ${age}`)
    )
}
```

Let's combine the above two using the .map() function and show them on the UI

```javascript
var dashboard = div(
    h1("Dashboard"),
    p("User details: "),
    ...users.map(user => Users(user.name, user.age))
)

render("app", dashboard)
```

{% hint style="info" %}
Note: You can use this system for stateful data as well, and whenever the stateful array will be updated, so will this one
{% endhint %}

### Looping over objects

{% hint style="danger" %}
Looping over objects are very slow, as we might need to re-iterate the keys and get their corresponding values, and then we will get the component ready, so unless it is not possible, avoid iterating objects
{% endhint %}

This time also, we will use a javascript function to loop over

* [Object.keys()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)

Now I will not go into details, but this function returns an array of keys of the object, that means we need to further iterate it using .map(), and then we have to access the value at that key in the object and return a component.

Let's see a simple example:

```javascript
let data = {
    name: "Mayukh",
    age: 17
}

var ObjectData = div(
    ...Object.keys(data).map(key => p(`${key}: ${data[key]}`))
)

render("app", ObjectData)
```

And there you have it! Congrats on reading through the Reactivity portion. This was a huge portion and I guess boring too, but you now know 90% of the entire ecosystem of Weblabs, and can start making apps.

There are two more advanced topics left, lifecycle and routing, and those are for quite complex use cases, though I would suggest learning routing too!

### References

Sometimes, we might need to work directly with a component, as in getting it's properties such as filling forms.

We can directly reference a component in Weblabs using the .ref() method. Since the component referenced is a HTMLComponent, every method and actions are valid for it as well.

Let's say I have an input, and I want to alert the user with the value of the input with the press of a button. Instead of creating a state that will store the value, we can reference the component itself and use the value property to get the reference as such:

```typescript
import { render, State } from "@weblabsjs/core";
import { button, div, input } from '@weblabsjs/core/components'

function RefTest() {
  const refData = State() //we will store the input component here as reference

  return div(
    input().prop("placeholder", "Enter some data").ref(refData),
    button("Get current value of input")
      .event("click", e => alert(refData.get().value)) //get the reference value
  )
}

render("app", RefTest())
```
