2024-05-22 21:43:49 +02:00
export const info = {
title : "Signals and reactivity" ,
description : "Handling reactivity in UI via signals." ,
} ;
2023-11-21 14:37:57 +01:00
2023-11-24 16:16:08 +01:00
import { el } from "deka-dom-el" ;
2024-05-22 21:43:49 +02:00
import { simplePage } from "./layout/simplePage.html.js" ;
2023-11-17 16:15:26 +01:00
import { example } from "./components/example.html.js" ;
2023-11-24 16:16:08 +01:00
import { h3 } from "./components/pageUtils.html.js" ;
2024-05-22 21:43:49 +02:00
import { mnemonic } from "./components/mnemonic/signals-init.js" ;
2023-11-24 17:02:16 +01:00
import { code } from "./components/code.html.js" ;
2023-11-17 16:15:26 +01:00
/** @param {string} url */
const fileURL = url => new URL ( url , import . meta . url ) ;
2023-11-21 14:37:57 +01:00
2023-11-17 16:15:26 +01:00
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page ( { pkg , info } ) {
const page _id = info . id ;
2023-11-24 16:16:08 +01:00
return el ( simplePage , { info , pkg } ) . append (
2024-05-22 21:43:49 +02:00
el ( "h2" , "Using signals to manage reactivity" ) ,
2023-11-24 16:16:08 +01:00
el ( "p" ) . append (
"How a program responds to variable data or user" ,
" interactions is one of the fundamental problems of programming." ,
" If we desire to solve the issue in a declarative manner," ,
2024-05-22 21:43:49 +02:00
" signals may be a viable approach." ,
2023-11-24 16:16:08 +01:00
) ,
2024-05-22 21:43:49 +02:00
el ( code , { src : fileURL ( "./components/examples/signals/intro.js" ) , page _id } ) ,
2023-11-24 16:16:08 +01:00
2024-05-22 21:43:49 +02:00
el ( h3 , "Introducing signals" ) ,
2023-11-24 16:16:08 +01:00
el ( "p" ) . append (
2024-05-22 21:43:49 +02:00
"Using signals, we split program logic into the three parts." ,
2023-11-24 16:16:08 +01:00
" Firstly (α ), we create a variable (constant) representing reactive" ,
" value. Somewhere later, we can register (β) a logic reacting" ,
2024-05-22 21:43:49 +02:00
" to the signal value changes. Similarly, in a remaining part (γ ), we" ,
" can update the signal value."
2023-11-24 16:16:08 +01:00
) ,
2024-05-22 21:43:49 +02:00
el ( example , { src : fileURL ( "./components/examples/signals/signals.js" ) , page _id } ) ,
2023-11-24 16:16:08 +01:00
el ( "p" ) . append (
"All this is just an example of " ,
el ( "a" , { textContent : "Event-driven programming" , href : "https://en.wikipedia.org/wiki/Event-driven_programming" , title : "Wikipedia: Event-driven programming" } ) ,
" and " ,
el ( "a" , { textContent : "Publish– subscribe pattern" , href : "https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern" , title : "Wikipedia: Publish– subscribe pattern" } ) ,
" (compare for example with " , el ( "a" , { textContent : "fpubsub library" , href : "https://www.npmjs.com/package/fpubsub" , title : "NPM package: fpubsub" } ) , ")." ,
" All three parts can be in some manner independent and still connected" ,
" to the same reactive entity."
) ,
el ( "p" ) . append (
2024-05-22 21:43:49 +02:00
"Signals are implemented in the library as functions. To see current value" ,
" of signal, just call it without any arguments " , el ( "code" , "console.log(signal())" ) , "." ,
" To update the signal value, pass any argument " , el ( "code" , "signal('a new value')" ) , "." ,
" For listenning the signal value changes, use " , el ( "code" , "S.on(signal, console.log)" ) , "."
2023-11-24 16:16:08 +01:00
) ,
el ( "p" ) . append (
"Similarly to the " , el ( "code" , "on" ) , " function to register DOM events listener." ,
" You can use " , el ( "code" , "AbortController" ) , "/" , el ( "code" , "AbortSignal" ) , " to" ,
2023-11-29 18:25:21 +01:00
" " , el ( "em" , "off" ) , "/stop listenning. In example, you also found the way for representing" ,
2024-05-22 21:43:49 +02:00
" “live” piece of code computation pattern (derived signal):"
2023-11-24 16:16:08 +01:00
) ,
2024-05-22 21:43:49 +02:00
el ( example , { src : fileURL ( "./components/examples/signals/computations-abort.js" ) , page _id } ) ,
2023-11-29 18:25:21 +01:00
2024-05-22 21:43:49 +02:00
el ( h3 , "Signals and actions" ) ,
2023-11-29 18:25:21 +01:00
el ( "p" ) . append (
2024-05-22 21:43:49 +02:00
el ( "code" , "S(/* primitive */)" ) , " allows you to declare simple reactive variables, typically" ,
2023-11-29 18:25:21 +01:00
" around " , el ( "em" , "immutable" ) , " " , el ( "a" , { textContent : "primitive types" , title : "Primitive | MDN" , href : "https://developer.mozilla.org/en-US/docs/Glossary/Primitive" } ) , "." ,
" " ,
"However, it may also be necessary to use reactive arrays, objects, or other complex reactive structures."
) ,
2024-05-22 21:43:49 +02:00
el ( example , { src : fileURL ( "./components/examples/signals/actions-demo.js" ) , page _id } ) ,
2023-11-29 18:25:21 +01:00
el ( "p" , "…but typical user-case is object/array (maps, sets and other mutable objects):" ) ,
2024-05-22 21:43:49 +02:00
el ( example , { src : fileURL ( "./components/examples/signals/actions-todos.js" ) , page _id } ) ,
2023-11-29 18:25:21 +01:00
el ( "p" ) . append (
"In some way, you can compare it with " , el ( "a" , { textContent : "useReducer" , href : "https://react.dev/reference/react/useReducer" , title : "useReducer hook | React docs" } ) ,
2024-05-22 21:43:49 +02:00
" hook from React. So, the " , el ( "code" , "S(<data>, <actions>)" ) , " pattern creates" ,
2023-11-29 18:25:21 +01:00
" a store “machine”. We can then invoke (dispatch) registered action by calling" ,
2024-05-22 21:43:49 +02:00
" " , el ( "code" , "S.action(<signal>, <name>, ...<args>)" ) , " after the action call" ,
" the signal calls all its listeners. This can be stopped by calling " , el ( "code" , "this.stopPropagation()" ) ,
2023-11-29 18:25:21 +01:00
" in the method representing the given action. As it can be seen in examples, the “store” value is" ,
" available also in the function for given action (" , el ( "code" , "this.value" ) , ")."
2023-11-24 16:16:08 +01:00
) ,
2023-11-29 18:25:21 +01:00
el ( h3 , "Reactive DOM attributes and elements" ) ,
el ( "p" , "There are on basic level two distinc situation to mirror dynamic value into the DOM/UI" ) ,
el ( "ol" ) . append (
el ( "li" , "to change some attribute(s) of existing element(s)" ) ,
el ( "li" , "to generate elements itself dynamically – this covers conditions and loops" )
) ,
2024-05-22 21:43:49 +02:00
el ( example , { src : fileURL ( "./components/examples/signals/dom-attrs.js" ) , page _id } ) ,
2023-11-29 18:25:21 +01:00
el ( "p" ) . append (
2024-05-22 21:43:49 +02:00
"To derived attribute based on value of signal variable just use the signal as" ,
" a value of the attribute (" , el ( "code" , "assign(element, { attribute: S('value') })" ) , ")." ,
2023-11-29 18:25:21 +01:00
" " , el ( "code" , "assign" ) , "/" , el ( "code" , "el" ) , " provides ways to glue reactive attributes/classes" ,
" more granularly into the DOM. Just use dedicated build-in attributes " , el ( "code" , "dataset" ) , ", " ,
el ( "code" , "ariaset" ) , " and " , el ( "code" , "classList" ) , "."
) ,
el ( "p" ) . append (
2024-05-22 21:43:49 +02:00
"For computation, you can use the “derived signal” (see above) like " , el ( "code" , "assign(element, { textContent: S(()=> 'Hello '+WorldSignal()) })" ) , "." ,
2024-01-05 16:49:05 +01:00
" " ,
2024-05-22 21:43:49 +02:00
"This is read-only signal its value is computed based on given function and updated when any signal used in the function changes."
2023-11-29 18:25:21 +01:00
) ,
el ( "p" ) . append (
2024-05-22 21:43:49 +02:00
"To represent part of the template filled dynamically based on the signal value use " , el ( "code" , "S.el(signal, DOMgenerator)" ) , "." ,
2023-11-29 18:25:21 +01:00
" This was already used in the todo example above or see:"
) ,
2024-05-22 21:43:49 +02:00
el ( example , { src : fileURL ( "./components/examples/signals/dom-el.js" ) , page _id } ) ,
2023-11-29 18:25:21 +01:00
el ( mnemonic )
2023-11-17 16:15:26 +01:00
) ;
}