Created
December 5, 2022 08:58
-
-
Save dliebner/71040dbcd97d2eb27aa92befa56ff281 to your computer and use it in GitHub Desktop.
GlobalSignalProvider for managing preact signals in Lit
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
// Requires https://lit.dev/playground/#gist=660427ac6065bed390c438b0eb5b5036 | |
class FollowUserButton extends SignalWatcher(LightElement) { | |
static properties = { | |
userId: {state: true}, | |
isFollowingInitial: {state: true}, | |
}; | |
/** @type {String} */ | |
_userId; | |
constructor() { | |
super(); | |
/** @type {boolean} */ | |
this.isFollowingInitial; | |
} | |
get userId() { | |
return this._userId; | |
} | |
set userId( val ) { | |
// Unsubscribe from any old signals | |
this.unsubscribeSignals(); | |
// Hold reference to old value | |
let oldVal = this._userId; | |
// Update the internal value | |
this._userId = val; | |
// Subscribe to signals | |
this.subscribeSignals(); | |
// Let component know the value has changed | |
this.requestUpdate('userId', oldVal); | |
} | |
followingUserSignal; | |
subscribeSignals() { | |
if( this.userId && !this.followingUserSignal ) { | |
this.followingUserSignal = GlobalSignalProvider.subscribe(this, GSP.SignalKeys.FollowingUser(this.userId), !!this.isFollowingInitial ); | |
} | |
} | |
unsubscribeSignals() { | |
if( this.userId && this.followingUserSignal ) { | |
GlobalSignalProvider.unsubscribe(this, GSP.SignalKeys.FollowingUser(this.userId) ); | |
this.followingUserSignal = undefined; | |
} | |
} | |
connectedCallback() { | |
super.connectedCallback(); | |
this.subscribeSignals(); | |
} | |
disconnectedCallback() { | |
super.disconnectedCallback(); | |
this.unsubscribeSignals(); | |
} | |
/** @param {MouseEvent} e */ | |
clicked(e) { | |
this.followingUserSignal.value = !this.followingUserSignal.value; | |
} | |
render() { | |
return html` | |
<button @click=${this.clicked}>${this.followingUserSignal.value ? 'Following' : 'Follow'}</button> | |
`; | |
} | |
} |
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
class GSP_WrappedSignal { | |
constructor(initialValue) { | |
this.subscribers = new WeakMap(); | |
this.preactSignal = preactSignals.signal(initialValue); | |
} | |
} | |
class GlobalSignalProvider { | |
// Use a Map to store the list of subscribers for each signal | |
static wrappedSignals = new Map(); | |
// Helper function for generating a signal key | |
static SignalKeys = { | |
FollowingUser: (userId) => `FollowingUser:${userId}`, | |
}; | |
// Helper function for getting a signal | |
/** @return {GSP_WrappedSignal} */ | |
static getWrappedSignal(signalKey) { | |
return this.wrappedSignals.get(signalKey); | |
} | |
// Helper function for creating a signal if it doesn't exist | |
static createSignalWrapper(signalKey, initialValue) { | |
let wrappedSignal = this.getWrappedSignal(signalKey); | |
if( !wrappedSignal ) { | |
wrappedSignal = new GSP_WrappedSignal(initialValue); | |
this.wrappedSignals.set(signalKey, wrappedSignal); | |
} | |
return wrappedSignal; | |
} | |
// Helper function for destroying a signal | |
static destroyWrappedSignal(signalKey) { | |
this.wrappedSignals.delete(signalKey); | |
} | |
// Function for subscribing to a signal | |
static subscribe(requester, signalKey, initialValue) { | |
// Create the signal if it doesn't exist | |
const wrappedSignal = this.createSignalWrapper(signalKey, initialValue); | |
// Add the requester to the list of subscribers for the signal | |
wrappedSignal.subscribers.set(requester, true); | |
// Return the preactSignal | |
return wrappedSignal.preactSignal; | |
} | |
// Function for unsubscribing from a signal | |
static unsubscribe(requester, signalKey) { | |
const wrappedSignal = this.getWrappedSignal(signalKey); | |
if( !wrappedSignal || !wrappedSignal.subscribers ) { | |
// Do nothing if the signal doesn't exist or there are no subscribers | |
return; | |
} | |
// Remove the requester from the list of subscribers | |
wrappedSignal.subscribers.delete(requester); | |
if( wrappedSignal.subscribers.size === 0 ) { | |
// Destroy the signal if there are no remaining subscribers | |
this.destroyWrappedSignal(signalKey); | |
} | |
} | |
} | |
// Create a shorthand reference to the GobalSignalProvider | |
const GSP = GlobalSignalProvider; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment