Last active
June 7, 2024 20:41
-
-
Save calebporzio/3153ddfc31ae5a00463b71b2fc938ca0 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
import { reactive } from '@vue/reactivity' | |
function switchboard(value) { | |
let lookup = {} | |
let current | |
let get = () => current | |
let set = (newValue) => { | |
if (newValue === current) return | |
if (current !== undefined) lookup[current].state = false | |
current = newValue | |
if (lookup[newValue] === undefined) { | |
lookup[newValue] = reactive({ state: true }) | |
} else { | |
lookup[newValue].state = true | |
} | |
} | |
let is = (comparisonValue) => { | |
if (lookup[comparisonValue] === undefined) { | |
lookup[comparisonValue] = reactive({ state: false }) | |
return lookup[comparisonValue].state | |
} | |
return !! lookup[comparisonValue].state | |
} | |
value === undefined || set(value) | |
return { get, set, is } | |
} | |
function switchboardSet(value) { | |
let lookup = {} | |
let current = [] | |
let all = () => current | |
let add = (newValue) => { | |
if (current.includes(newValue)) return | |
current.push(newValue) | |
if (lookup[newValue] === undefined) { | |
lookup[newValue] = reactive({ state: true }) | |
} else { | |
lookup[newValue].state = true | |
} | |
} | |
let remove = (newValue) => { | |
if (! current.includes(newValue)) return | |
current = current.filter(i => i !== newValue) | |
if (lookup[newValue] === undefined) { | |
lookup[newValue] = reactive({ state: false }) | |
} else { | |
lookup[newValue].state = false | |
} | |
} | |
let has = (comparisonValue) => { | |
if (lookup[comparisonValue] === undefined) { | |
lookup[comparisonValue] = reactive({ state: false }) | |
return lookup[comparisonValue].state | |
} | |
return !! lookup[comparisonValue].state | |
} | |
let clear = () => { | |
for (let i = 0; i < current.length; i++) { | |
let key = current[i] | |
delete current[i] | |
lookup[key].state = false | |
} | |
current = current.filter(i => i) | |
} | |
value === undefined || value.forEach(i => add(i)) | |
return { all, add, remove, has, clear } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is super cool, I'm building a
useGrid
Vue composable to power interactive grids, and will definitely keep an eye on this pattern for optimization in the future.If used in a Vue setup function, the "gotcha" with
reactive-switchboard
is that you can't/shouldn't create reactive state or effects outside of a component'ssetup
function, for example, when dynamically adding new rows to the table and registering them in the switchboard lookup + setting up new watchers. Doing this in a Vue component would give you memory leaks and/or strange bugs and unpredictability.It's a solved problem though—you can use
effectScope
to fix it. With that, you'd scope all the dynamic runtimereactive
stuff, and you'd also return a thin wrapper aroundwatchEffect
to make sure effects run in the same scope. You can also return astop
function so people can clean up thereactive-switchboard
scope on demand.This fork shows what I mean (I haven't tested any of this): https://gist.github.com/AlexVipond/4fd4591deebc643b7e3bd9a5a9194599