-
-
Save getify/6bb0163f3c2768171389142f627cfb08 to your computer and use it in GitHub Desktop.
experiment: mimicking React's new "useState()" hook for stand-alone functions, including "custom hooks"
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
"use strict"; | |
[foo,bar] = TNG(foo,bar); | |
// NOTE: intentionally not TNG(..) wrapping useBaz(), so that it's | |
// basically like a "custom hook" that can be called only from other | |
// TNG-wrapped functions | |
function foo(origX,origY) { | |
var [x,setX] = useState(origX); | |
var [y,setY] = useState(origY); | |
useBaz("foo"); // calling useBaz(..) like it's a "custom hook" | |
console.log(`foo: ${x} ${y}`); | |
setX( x * 2 ); | |
setY( bar(y) ); | |
return origX; | |
} | |
function bar(curY) { | |
var [z,setZ] = useState(curY + 1); | |
useBaz("bar"); // calling useBaz(..) like it's a "custom hook" | |
console.log(`bar: ${z}`); | |
z = z * curY; | |
setZ( z ); | |
return z; | |
} | |
// since useBaz(..) isn't TNG-wrapped, it's like a "custom hook", in that | |
// you don't call it stand-alone, but rather only call it from another | |
// TNG-wrapped function. thus, useBaz(..) uses the context of the calling | |
// TNG-wrapped function for its useState(..) calls. | |
function useBaz(which) { | |
var [count,setCount] = useState(0); | |
count++; | |
setCount(count); | |
console.log(`${which} count: ${count}`); | |
} | |
foo(3,9); | |
// foo count: 1 | |
// foo: 3 9 | |
// bar count: 1 | |
// bar: 10 | |
foo(); | |
// foo count: 2 | |
// foo: 6 90 | |
// bar count: 2 | |
// bar: 90 | |
bar(42); | |
// bar count: 3 <--- only updated the `count` state of bar(..) | |
// bar: 8100 | |
foo(); | |
// foo count: 3 <--- `count` state is separate between foo(..) and bar(..) | |
// foo: 12 8100 | |
// bar count: 4 <--- ditto | |
// bar: 340200 |
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
var { TNG, useState } = (function def(){ | |
"use strict"; | |
var buckets = new WeakMap(); | |
var currentBucket = []; | |
return { TNG, useState }; | |
// ****************** | |
function TNG(...fns) { | |
fns = fns.map(function mapper(fn){ | |
return function tngf(...args) { | |
if (buckets.has(tngf)) { | |
let bucket = buckets.get(tngf); | |
bucket.nextIdx = 0; | |
} | |
currentBucket.push(tngf); | |
try { | |
return fn.apply(this,args); | |
} | |
finally { | |
currentBucket.pop(); | |
} | |
}; | |
}); | |
if (fns.length < 2) return fns[0]; | |
return fns; | |
} | |
function useState(defVal) { | |
if (currentBucket.length > 0) { | |
let tngf = currentBucket[currentBucket.length - 1]; | |
let bucket; | |
if (!buckets.has(tngf)) { | |
bucket = { nextIdx: 0, slots: [], }; | |
buckets.set(tngf,bucket); | |
} | |
bucket = buckets.get(tngf); | |
if (!(bucket.nextIdx in bucket.slots)) { | |
let slot = [ | |
typeof defVal == "function" ? defVal() : defVal, | |
function updateSlot(v){ slot[0] = v; } | |
]; | |
bucket.slots[bucket.nextIdx] = slot; | |
} | |
return [...bucket.slots[bucket.nextIdx++]]; | |
} | |
else { | |
throw new Error("Only use useState() inside of (or functions called from) TNG-wrapped functions."); | |
} | |
} | |
})(); |
I've published this experiment as an actual project/package, called "TNG-Hooks":
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Some examples:
https://codepen.io/getify/pen/dQvEGW?editors=1010
https://codepen.io/getify/pen/VVbZOd?editors=1010