Created
January 4, 2021 17:50
-
-
Save WebReflection/77b005e5693cf5e5c1fa1ea781b673d8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// WARNING: There's much more to know/do around hooks, and | |
// this is just a simplification of how these work. | |
// shared references, updated | |
// per each hook invoke | |
let execution = null; | |
let current = null; | |
let context = null; | |
let args = null; | |
// returns a function that updates known | |
// references each time it gets executed | |
// so that using useState within such function | |
// can retrieve all details related to the hook | |
const hooked = callback => { | |
// create a stack to incrementally retrieve | |
// or add new states whenever it's needed | |
const details = {i: 0, stack: []}; | |
// return the hook function that resets index, | |
// update shared references, and then executes | |
return function hook() { | |
// reset the stack index | |
details.i = 0; | |
// update shared references | |
execution = details; | |
current = hook; | |
context = this; | |
args = arguments; | |
// invoke the original callback | |
return callback.apply(context, args); | |
}; | |
}; | |
// every time a hook invokes a useState | |
// the initial value is either known or new | |
const useState = value => { | |
// trap in this closure current details | |
// so that it's possible to re-invoke the hook | |
// with latest provided context and arguments | |
const hook = current; | |
const ctx = context; | |
const rest = args; | |
// grab current stack and its index | |
const {i, stack} = execution; | |
// increment the stack index so that the next | |
// useState(value) will get its slot | |
execution.i++; | |
// if the stack does not know about this slot | |
if (i === stack.length) { | |
// store the initial value in the stack, | |
// making the current stack[i] reachable | |
stack.push(value); | |
} | |
return [ | |
// the last value stored at this index | |
// or the one we've just pushed | |
stack[i], | |
// the update function that re-invoke the hook | |
value => { | |
// update this stack value with a new one | |
stack[i] = value; | |
// execute the hook one more time, | |
// so that everything gets synchronized | |
hook.apply(ctx, rest); | |
// this will repeat this recursive dance | |
// until no state update happens | |
} | |
]; | |
}; | |
// EXAMPLE - Counter factory | |
// the hooked callback can be defined a part | |
// as long as it's invoked only once hooked | |
const toBeWrapped = start => { | |
const [num, update] = useState(start); | |
console.log(`Counting: ${num}`); | |
setTimeout(update, 1000, num + 1); | |
}; | |
// in this case the Counter returns, each time, | |
// a new hooked version of the same callback | |
// and it executes it right away | |
function Counter() { | |
return hooked(toBeWrapped).apply(this, arguments); | |
} | |
// read a counting from 0 to N in console | |
Counter(0); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For a better, working, essential, alternative, see uhooks/e export 👋