#fuck_jews's avatar
#fuck_jews 4 months ago
Note 1 i talked a lot about proper nostr fetchings and relays management, how to avoid double fetch, how to leverage filters 'since' attribute for less time+bandwidth what made this pursuit so tedious wasn't the aspect of fetchings but rather the hell of jumping between online fetches and the DB retrievals data journey see, whenever you save on bandwidth, DB must be there for you *SUPER WISDOM it would be easier to track the solidity of a change when things reflects expectedly Note 2 we can solve this problem with signals, more specifically Solid Signals what is signals? your ordinary React component goes like this: const MyComponent = () => { const [age, setAge] = useState(28); retutn <span>{age}</span> } the equiveillance in signals land: const [age, setAge] = createSignal(28); coonst MyComponent = () => <span>{age}</span> or in Vanilla: const [age, setAge] = createSignal(28); const span = document.createElement("span"); container.appendChild(span); const dispose = createRoot( (disposeEffect) => ( createEffect(() => (span.textContent = age())), () => (span.remove(), disposeEffect()) ), ); here is a plug and play signals-example.html in vanilla what happened there? -signals are simply React hooks that can be used outside components -effects, created by 'createEffect' is a subtle reactive window, it reruns when a signal used inside it updates effects don't keep track of signals inside them but rather each signal tracks effects it runs inside and rerun these effects on update that makes Solid Signals dynamic and an undisputed king of performance so createEffect(() => true ? sig1() : sig2()) will only rerun when sig1() is updated createEffect(() => plain.condition ? sig1() : sig2()) might give an unexpected behavior lets say -1st run: plain.condition is true, sig1() fired -further down the line plain.condition flips into false -then sig1() updates -2nd run: sig2() is fired, sig1() untracks that effect, so we got another rerun triggered by sig2() though it didn't run this time, that's unpleasant now lets forget about the UI for a bit, Solid Signals wasn't designed for UI as a first thought, the UI library SolidJS is just a babel wiring of effects from nice jsx components into Vanilla Note 3 In NOSTR client apps, we have 3 cases of data: 1-its in-memory, everything is fine 2-its in DB, lets retrieve it 3-its neither, hopefully we fetch it then parse it what happens when we replace plain structs into signals structs in nostr? var note = { id: '123', content: createSignal('NOSTR is pure signals'), // instead of 'NOSTR is pure signals' created_at: createSignal(9999999999) // instead of 999999999, ..... } now we don't care where the data is or where its coming from, we can initialize every note properties into the default and let signals magic take over to reflect everywhere var note = { id: '123', content: createSignal(), created_at: createSignal(), ..... } now we would have more pleasant UI hydration and also our data became reactive What's good about reactive data? we can have deferred operations/peeks on data no matter where it was lets say we want to peek at note with id '123fuckjews321' created_at field, we don't know if we have it on DB or not, its a hell to always try to handle the 3 situations every where we need just a peek so instead of working with plain variables const latestTimestamp = getNote(lastNoteId).created_at; if (event.created_at > latestTimestamp) update_sorted_notes(event.created_at, latestTimestamp); we can do reactive code such as RunOnceAfterHydration( () => getNote(lastNoteId).created_at[solid.get](), (latestTimestamp) => latestTimestamp !== 0, // 0 means not hydrated yet (latestTimestamp) => update_sorted_notes(event.created_at, latestTimestamp), ); where 'RunOnceAfterHydration' is just an effect that disposes itself after hydration: function RunOnceAfterHydration<T>( target: () => T, isReady: (val: T) => boolean, clb: (val: T) => void, ) { createRoot((dispose) => { createEffect(() => { const val = target(); isReady(val) && (clb(val), dispose()); }); }); } what about aggregated derived values? we can follow the same patter of reactive code, its easy construct your own custom reactive handling, template it to your needs, compute eagerly, lazy, continuously or one time shot as we did above and not worry about the 3 cases of nostr data anymore BRACES ARE UGLY, I FIND IT COMPLEX AND HARD TO READ? no its not, its just a programing paradigm new to you, once you spent a little time it gets comfy like it? check out this aggregation signal i created days ago image