Created
June 19, 2020 19:49
-
-
Save bendemboski/cb2a472da3a0f6bb7b8d1f79f7e1419d to your computer and use it in GitHub Desktop.
code to make getters act like @Tracked state
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 { tracked } from '@glimmer/tracking'; | |
// | |
// A tracked macro is a getter (with optional setter) that acts as tracked state. | |
// The usage is as follows: | |
// | |
// import { trackedMacro, dirtyTrackedMacro } from 'tracked-macro'; | |
// | |
// class MyClass { | |
// externalLibrary = new ExternalLibraryThatIsNotTrackedAware() | |
// | |
// @trackedMacro | |
// get externalState() { | |
// return this.externalLibrary.getState(); | |
// } | |
// // setter is optional | |
// set externalState(value) { | |
// return this.externalLibrary.setState(value); | |
// } | |
// | |
// constructor() { | |
// this.externalLibrary.addListener('change:state', () => { | |
// dirtyTrackedMacro(this, 'externalState'); | |
// }); | |
// } | |
// } | |
// | |
// This will cause `externalState` to act as if it were @tracked, even though | |
// its actually state that is derived from non-tracked state. The getter gets | |
// the underlying value and the setter sets it, both in @tracked-aware ways, and | |
// if the underlying state can change through other non-@tracked aware | |
// mechanisms, when we are notified of such a change, `dirtyTrackedMacro()` can | |
// be used to notify the tracking infrastructure. | |
// A class used to hook into the tracking infrastructure. The @tracked tag | |
// doesn't hold any meaningful state, but by reading it and writing to it at the | |
// same time as we read/write to a piece of untracked state, we can effectively | |
// make the tracking infrastructure track that untracked state. | |
class Tag { | |
@tracked tag | |
consume() { | |
this.tag; | |
} | |
dirty() { | |
this.tag = undefined; | |
} | |
} | |
// A weak map mapping consumer objects to property-name-to-tag maps. The keys | |
// are consumer objects with @trackedMacro properties, and the values are Maps | |
// whose keys are the names of the @trackedMacro properties and values are Tag | |
// objects. | |
const tags = new WeakMap(); | |
// Helper to get or create a Tag for a given consumer object and @trackedMacro | |
// property key | |
function getTag(obj, key) { | |
let tagsForObject = tags.get(obj); | |
if (!tagsForObject) { | |
tagsForObject = {}; | |
tags.set(obj, tagsForObject); | |
} | |
let tag = tagsForObject[key]; | |
if (!tag) { | |
tag = new Tag(); | |
tagsForObject[key] = tag; | |
} | |
return tag; | |
} | |
// Our @trackedMacro decorator | |
export function trackedMacro(obj, key, descriptor) { | |
let getter = descriptor.get; | |
let setter = descriptor.set; | |
descriptor.get = function(...args) { | |
getTag(this, key).consume(); | |
return getter.call(this, ...args); | |
}; | |
descriptor.set = function(...args) { | |
getTag(this, key).dirty(); | |
return setter.call(this, ...args); | |
}; | |
} | |
// A function that will dirty a @trackedMacro property on an object | |
export function dirtyTrackedMacro(obj, key) { | |
getTag(obj, key).dirty(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment