Skip to content

3. Signals (Reactivity) and Dynamic Render

NotElementImport edited this page Nov 10, 2024 · 6 revisions

Base

There are mechanisms in the framework that allow you to achieve application reactivity.
First, we need to understand how reactivity works:

The Signal is responsible for the reactivity. Signal returns a structure with the value property. Which, when changed, sends an event for changes in the Signal object. That allows to work with small details in the framework in a rather optimized way.

Practice:

import { comp } from "horizon-core/component";
import { useSignal } from "horizon-core/state";

export default comp((_, { text, $ }) => {
    // Creating Signal
    const counter = useSignal(0)

    // Creating button with click event and text like `Counter: ${counter.value}`
    $('button', { '@click': () => counter.value++ }, () => {
        text('Counter: ')
        text(counter)
    })
})

As Raw (Custom data for DOM)

To display your information in the DOM element. You can use the asRaw method in the configuration

import { comp } from "horizon-core/component";
import { useSignal } from "horizon-core/state";

export default comp((_, { text, $ }) => {
    const name = useSignal('jonh doe', {
        asRaw: value => `Hello ${value}!`
    })

    text(name)
})

And the output will be

<span>Hello jonh doe!</span>

Or another example:

import { comp } from "horizon-core/component";
import { useSignal } from "horizon-core/state";

export default comp((_, { text, $ }) => {
    const blockConfig = useSignal({}, {
        asRaw: value => `display: ${value.display}; width: ${display.width}em;`
    })

    blockConfig.display = 'flex'
    blockConfig.width   = 10

    $('div', { style: blockConfig }, () => {
        // ...Block content...
    })
})

And the output will be

<div style="display: flex; width: 10em;">
    <!--...Block content...-->
</div>

And all of the examples have reactivity

Observe Mutation Signal

To keep track of Signal changes, you need to use the global method watch()

import { comp } from "horizon-core/component";
import { useSignal, watch } from "horizon-core/state";

export default comp((_, { text, $ }) => {
    const counter = useSignal(0)

    const unwatch = watch(counter, value => {
        console.log(`Counter: ${value}`)
    })

    counter.value++
    // Counter: 1

    unwatch()

    counter.value++
})

Work with object/arrays

import { comp } from "horizon-core/component";
import { useSignal, watch } from "horizon-core/state";

export default comp((_, { text, $ }) => {
    const data = useSignal({
        first: 'test 1',
        second: {
            value: 'test 2'
        }
    })

    watch(data, value => {
        console.log(`Without deep: ${value}`)
    })

    watch(data, value => {
        console.log(`With deep: ${value}`)
    }, { deep: true })

    data.value.first = 'New value test'
    // Without deep: { "first": "New value test", "second": { value: "test 2" } }
    // With deep: { "first": "New value test", "second": { value: "test 2" } }

    data.value.second.value = 'Deep value test'
    // With deep: { "first": "New value test", "second": { value: "Deep value test" } }
})

Local property observe:

import { comp } from "horizon-core/component";
import { useSignal, watch } from "horizon-core/state";

export default comp((_, { text, $ }) => {
    const person = useSignal({
        lastName: 'Doe',
        firstName: 'Jonh'
    })

    watch(person.value.firstName, value => {
        console.log(`New name: ${value}`)
    })

    person.value.firstName = 'Mike'
    // New name: Mike
})

Clone this wiki locally