Last active
December 31, 2024 11:44
-
-
Save calebporzio/3153ddfc31ae5a00463b71b2fc938ca0 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
useGridVue 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-switchboardis that you can't/shouldn't create reactive state or effects outside of a component'ssetupfunction, 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
effectScopeto fix it. With that, you'd scope all the dynamic runtimereactivestuff, and you'd also return a thin wrapper aroundwatchEffectto make sure effects run in the same scope. You can also return astopfunction so people can clean up thereactive-switchboardscope on demand.This fork shows what I mean (I haven't tested any of this): https://gist.github.com/AlexVipond/4fd4591deebc643b7e3bd9a5a9194599