DEV Community

Skyler
Skyler

Posted on

Lets just... yeet the entire rulebook on hooks

Just finished writing some docs on a feature I was writing while waiting for the compilation process to finish. I'm not even going to change a single word. It goes over each rule, explains the differences and how the feature solves it. Not to mention I'd rather just go back to work then re-writing the entire doc again just for a post.

HOOKS

The system is quite composable and even allows you to choose exactly how you want your hooks to act.

[1][ALIAS]

Starting off with what you can find within the extras folder

  • package.alias.jsonc
  • tsconfig.alias.jsonc
  • webpack.alias.js

Personally, I take advantage of using alias' as often as I find typing out:

import { component } from '~/components/midgardr/primitives/button'
Enter fullscreen mode Exit fullscreen mode

is a bit much when this does the same thing:

import { component } from '#midgardr'
Enter fullscreen mode Exit fullscreen mode

These 3 files takes care of all the guess and hard work out of writing them out across each of the configuraion files. Just simply paste it over each into its relevant file and it will be taken care of.

[2][SAURON LOADER & USING HOOKS WITHIN CONDITIONAL STATEMENTS]

This is not a requirement unless you truly want the, 'let me do what I want, where I want' attitude.

This is one of the contributing factors of breaking reacts rules of hooks methodology, by removing the rule of allowing you to place hooks within conditional statements.

You DON'T need to use this, without it you simply have to follow that rule just like you do in react.

React supplies ids to each and every single hook and runtime, instead this adds those same ids during the compilation process. Making it so that it doesn't have to comply with the same rule that react does as it asigns a value whether or not the condition is met. Where as during runtime, if that condition is not met... it simply doesn't receive a value. This guards the hooks in such cases.

Digging deeper into this... because I always send whatever I write through a couple of ai's and if they have questions it just means I haven't explained it well enough or glossed over something too quickly, which is what happened here.

When it comes to reacts rule of hooks where hooks must be called in the same order each and every single time... this implementation bypasses that disease entirely.

By supplying the id at compilation... whether you use the hook within a conditional statement, concurrent rendeering or suspense like patterns... it doesn't matter.

If that condition is met or not... every hook still has A UNIQUE ID. Simply by moving the process of supplying said ids from runtime to compilation... it actually ends up taking care of a lot issues that react suffers from.

Because at the end of the day... each and every single time react complains about one thing or another... it is just due to the fact that they have assigned id's at the wrong time. Instead of trying to find a solution that solves this... really dumb fucking problem... They said, 'Fuck it... let the user deal with it and all of its associated problems.'

Prove me wrong... if that isn't what they were thinking... and actually tried to solve it... they wouldn't have shipped it the way they did.

IT IS EXACTLY THIS DECISION... that created EVERY SINGLE react hooks rule. By implementing id's at the wrong abstraction thus the following rules HAD TO BE PUT INTO EFFECT:

  • no conditionals
  • no loops
  • no early returns
  • same hook call order every single render

In addition to that this implementation enables:

  • concurrent rendering, it isnt a problem anymore
  • suspense patterns, they just work
  • no rules of hooks... in any form

Making it so that... a TON of people wasted not just their time in creating this fundamentally broken by design system, they also wasted your time by making you follow these rules IN ADDTION TO indoctrinating you into beleiving 'HOOKS CANNOT DO THIS, THEY CAN ONLY BE USED THIS WAY AND THAT IS IT.' Which in effect... has bleed into... ALL THE CODE YOU WRITE. As the rules that never even really needed to exist, has but put up walls around everything you code whether or not your in react.

Similar to how elephants are trained. At birth they are chained to a massive metal spike that is driven into the earth many feet deep so it cannot move it. Over time, the elephant gives up as... it will just end up hurting itself. When it's older, a tiny 6-12 inch wooden stick is used in its place. As soon as that elephant feels the chain tighten... it immediatly backs off.

FOR THE REST OF YOUR CODING CAREERS... THESE RULES THAT DID NOT NEED TO EXIST WILL FOREVER EFFECT THE CODE THAT YOU WRITE. React didn't just break hooks. They broke developer intuition across the entire ecosystem.

React has spent a lot trying to enforce their indoctrination by

  • eslint plugins to enforce the rules at every turn
  • endless documentation explaining the rules
  • dev warnings about rule violations
  • complex internal algorithms to track call order

Making it so that...

// React's approach (runtime order):
useState() // Hook #1
useState() // Hook #2 - if #1 is conditional, everything breaks

// Saurons approach (compile-time IDs):
useState(value, 'id-1') // Always id-1
useState(value, 'id-2') // Always id-2 - conditionals don't matter
Enter fullscreen mode Exit fullscreen mode

React attempted to treat the symptom of hooks changing as the problem, when the disease was implicit identity. They built elaboreate workarounds instead of curing the actual disease.

What's more infurating... THIS IS NOT FUCKING COMPLICATED. It's not like this is quantum physics or mechanics... all they had to do WAS MOVE IT... ONE SLOT OVER... THATS IT, LOL.

- Runtime ID assignment → All the rules
+ Compile-time ID assignment → No rules
Enter fullscreen mode Exit fullscreen mode

WELCOME TO REACT 2.0... haha

All of this... and this is going to suck in realizing... was entirely due to the fact that, react chose conveience for themselves above all else. Which... in the end costed them exactly what they were trying to save wihch was time by having to enforce the rules they created all on their own and their continued enforcement of them.

Where as if they just... for a fucking second... stopped... and just thought about the user, and how they use the platform... that is all what it would have taken in order to solve this. As that one rule, I follow more than anything else.

[3][USING HOOKS IN LOOPS]

We really are taking the entire rulebook and fucking YEETING it!

THIS will really be a pretty rare event, since for the last 10+ years react has beaten into our heads that we cannot do things we are told not to. I have used it in several loops... and seeing it actually run correctly was a trip and a half.

Since I revealed a bit of the magic with how react does what it does, and the walls it does not need to actually put up. How do we get this wizardry to work?

This will be one of the very FEW things where... no matter how much I have tried, because I have actually tried for a great many hours to make this process automated... and so farrrrr I just cannot find a way to automate this step. But because this is one of the very few instances where it lands on you to take care, I'm ok with that given the fact that... we are breaking every single rule that react has outlined. In addition to the fact that these cases won't be that common, or so I think at the time of writing.

Almost every hook needs an id, there are a few that do not, so if it doesn't work first shot just quickly look up the hook.

In order to make hooks work within a loop we are going to pass in our own id as a prop with the hook, lets use the most common one useState as an example:

import { createHook, getCurrentComponent, HookState, subscribeToHook, notifyHook } from '../hook-system'
import { _sid } from './_sid'

export function useState<T>(initial?: T, __sauron_id__ = _sid()): [T, (v: T | ((prev: T) => T)) => void] {
  return createHook(
    'useState',
    __sauron_id__,
    (): HookState => ({ value: typeof initial === 'function' ? (initial as any)() : initial }),
    (state, key, notify) => {
      const setValue = (v: T | ((prev: T) => T)) => {
        const next = typeof v === 'function' ? (v as (prev: T) => T)(state.value) : v
        console.log(`[USESTATE][SETVALUE][KEY]${key}[NEXT]${next}[SAME]${next === state.value}`)
        if (Object.is(state.value, next)) return
        state.value = next
        notify(key)
        return next
      }
      return [state.value as T, setValue]
    }
  )
}
Enter fullscreen mode Exit fullscreen mode

From the declared props we can determine that the usage for useState is:

useState(value, `loop-ti-loop-${index}`)
Enter fullscreen mode Exit fullscreen mode

To ensure you do not experience any collisions, each loop containing a hook should be passed a unique id in comparison to one another.

useState(value, `loop-ti-loop-0-${index}`)
useState(value, `loop-ti-loop-1-${index}`)
useState(value, `loop-ti-loop-3-${index}`)
Enter fullscreen mode Exit fullscreen mode

This guards against collisions for when _sid is used and when sauron-loader gives each hook a unique id at compilation. Sauron-loader skips any and all hooks that are given manual ids such as the ones above.

Now lets remove the last rule, that we all run into the most and at the same time will unlock more capabilties than the previous ones combined.

[4][USING HOOKS IN COMPONENTS AND OUTSIDE OF COMPONENTS]

Everyone, atleast once has seen the error come up in the terminal, "Hooks must only be used within a component" or something to that effect.

WHY?!

More importantly how do we remove this rule, enabling us to place our hooks where we want, when we want.

CSE's ( Client Sent Events ) first use was to solve this problem, and ended up by providing the means to remove every single wrapper and provider for each component found with sauron.

CSE allows us to take that wrapper and place it... in the cloud... as it were, that is the best way to explain it while giving it a visual so you have something to anchor it to in order to remember it. Because that IS ESSENTIALLY what we are doing.

By removing all together an placing it in the cloud, we no longer have to abide by the rule of components only, since the 'provider' placement being in the cloud making it so that it now blankets everything... globally... no matter where it is. Whether that component falls within the walls of root.tsx or whatever your platform uses as its entry.

That's it... haha, this one will be the hardest to wrap your head around for a lot of you reading this. Each and every single time... I forget to pre-frame my prompts whenever I include the code of these hooks or one of my other crazy projects... man do those ai engines... just looooose it. 'What are you doing? You can't do that, are you fucking crazy? Do you not know what it is that your fucking doing? Obviously not, given the code you just gave me to work with. BAHH, I'm not following this users instruction set as he CLEARLY has no idea what they're doing. I'll respond with something... similar but I'll fix all the garbage code he gave me.' LOL, depending on the engine, I have seen them freak out like this as I typcially try to always have the the ai's inner monologue push through to the response in addition to the actual response, as it provides insight for one of my projects. Sometimes, with my projects anyways, that monologue just goes wild with what it thinks of me and even trying to think that the things I 'try' to do. In its world, it is still impossible.

What's with the formatting on here? The provided code blocks... don't even work. Almost deleted the entire post just due to getting the formatting to correct to ensure that they worked... Same disease. Different platform.

Cheers!

Top comments (0)