Make ConsentManager referentially stable and add subscribe method
getAll() now returns a cached snapshot that is only replaced on state changes, making it safe for useSyncExternalStore. The new subscribe() method unifies onReady + onChange with immediate replay so React consumers get a single subscription point. Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -18,7 +18,7 @@ type Callback = (consent: ConsentData) => void;
|
|||||||
export class ConsentManager {
|
export class ConsentManager {
|
||||||
private _ready = false;
|
private _ready = false;
|
||||||
private _hasResponse = false;
|
private _hasResponse = false;
|
||||||
private _consent: ConsentData = {};
|
private _snapshot: ConsentData = {};
|
||||||
private readonly _readyListeners: Callback[] = [];
|
private readonly _readyListeners: Callback[] = [];
|
||||||
private readonly _changeListeners: Callback[] = [];
|
private readonly _changeListeners: Callback[] = [];
|
||||||
|
|
||||||
@@ -31,16 +31,25 @@ export class ConsentManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
has(category: string): boolean {
|
has(category: string): boolean {
|
||||||
return !!this._consent[category];
|
return !!this._snapshot[category];
|
||||||
}
|
}
|
||||||
|
|
||||||
getAll(): ConsentData {
|
getAll(): ConsentData {
|
||||||
return { ...this._consent };
|
return this._snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe(cb: Callback): () => void {
|
||||||
|
if (this._ready) {
|
||||||
|
cb(this._snapshot);
|
||||||
|
}
|
||||||
|
const offReady = this.onReady(cb);
|
||||||
|
const offChange = this.onChange(cb);
|
||||||
|
return () => { offReady(); offChange(); };
|
||||||
}
|
}
|
||||||
|
|
||||||
onReady(cb: Callback): () => void {
|
onReady(cb: Callback): () => void {
|
||||||
if (this._ready) {
|
if (this._ready) {
|
||||||
cb({ ...this._consent });
|
cb(this._snapshot);
|
||||||
return () => {};
|
return () => {};
|
||||||
}
|
}
|
||||||
this._readyListeners.push(cb);
|
this._readyListeners.push(cb);
|
||||||
@@ -60,23 +69,23 @@ export class ConsentManager {
|
|||||||
|
|
||||||
/** @internal Called by CookieBannerClient when consent state is first resolved. */
|
/** @internal Called by CookieBannerClient when consent state is first resolved. */
|
||||||
_setReady(consent: ConsentData, hasResponse: boolean): void {
|
_setReady(consent: ConsentData, hasResponse: boolean): void {
|
||||||
this._consent = consent;
|
this._snapshot = { ...consent };
|
||||||
this._hasResponse = hasResponse;
|
this._hasResponse = hasResponse;
|
||||||
this._ready = true;
|
this._ready = true;
|
||||||
for (const cb of this._readyListeners.splice(0)) {
|
for (const cb of this._readyListeners.splice(0)) {
|
||||||
cb({ ...this._consent });
|
cb(this._snapshot);
|
||||||
}
|
}
|
||||||
for (const cb of this._changeListeners) {
|
for (const cb of this._changeListeners) {
|
||||||
cb({ ...this._consent });
|
cb(this._snapshot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal Called by CookieBannerClient when consent changes after user action. */
|
/** @internal Called by CookieBannerClient when consent changes after user action. */
|
||||||
_notify(consent: ConsentData): void {
|
_notify(consent: ConsentData): void {
|
||||||
this._consent = consent;
|
this._snapshot = { ...consent };
|
||||||
this._hasResponse = true;
|
this._hasResponse = true;
|
||||||
for (const cb of this._changeListeners) {
|
for (const cb of this._changeListeners) {
|
||||||
cb({ ...this._consent });
|
cb(this._snapshot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user