Skip to content

Instantly share code, notes, and snippets.

@janeklb
Created October 31, 2024 08:36
Show Gist options
  • Save janeklb/dd6ac02a03efbaeccec59eb6890836d1 to your computer and use it in GitHub Desktop.
Save janeklb/dd6ac02a03efbaeccec59eb6890836d1 to your computer and use it in GitHub Desktop.
/* eslint-disable no-console */
import { useEffect } from 'react';
type ShouldLog = (name: string) => boolean;
type Stats = {
readonly name: string;
count: number;
previousState?: unknown;
};
function stringify(input: unknown): string {
return typeof input === 'function' ? '[fn]' : JSON.stringify(input);
}
export class ChangeDetector {
private readonly statsMap = new Map<string, Stats>();
constructor(
private readonly label: string,
private readonly shouldLog?: ShouldLog,
) {}
protected getStats(name: string): Stats {
let stats = this.statsMap.get(name);
if (!stats) {
stats = { name, count: 0 };
this.statsMap.set(name, stats);
}
return stats;
}
public tick(name: string, state?: unknown): void {
const stats = this.getStats(name);
if (!this.shouldLog || this.shouldLog(name)) {
this.logStats(state, stats);
}
stats.previousState = state;
stats.count++;
}
private logStats(state: unknown, { name, count, previousState }: Stats): void {
const currJson = stringify(state);
if (count === 0) {
console.debug(`[${this.label}] ${name} (initialized)`, count, { curr: currJson });
} else if (state === previousState) {
console.log(`[${this.label}] ${name} ✅ (not changed)`, count, { curr: currJson });
} else {
const prevJson = stringify(previousState);
if (prevJson === currJson) {
console.log(`[${this.label}] ${name} ❗️ (changed, but stringified equal)`, count, { curr: currJson });
} else {
console.log(`[${this.label}] ${name} 🔀 (changed)`, count, { prev: prevJson, curr: currJson });
}
}
}
}
export function createEffectChangeLogger(label: string, shouldLog?: ShouldLog) {
const changeDetector = new ChangeDetector(label, shouldLog);
return function useEffectChangeLogger(name: string, state: unknown) {
useEffect(() => changeDetector.tick(name, state), [name, state]);
useEffect(() => () => console.log('unmounting', name), [name]);
};
}
export const useGlobalEffectChangeLogger = createEffectChangeLogger('global');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment