react-smart-effect
Version:
Enhanced React useEffect and useLayoutEffect hooks with smart dependency tracking, debugging tools, and automatic optimization
145 lines (144 loc) • 5.38 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.deepCompareDeps = deepCompareDeps;
exports.analyzeDeps = analyzeDeps;
exports.generateDependencyWarning = generateDependencyWarning;
exports.logDependencyChanges = logDependencyChanges;
exports.createEffectId = createEffectId;
exports.formatDependencyValue = formatDependencyValue;
const lodash_isequal_1 = __importDefault(require("lodash.isequal"));
/**
* Deep comparison function for dependency arrays
*/
function deepCompareDeps(prevDeps, nextDeps) {
if (!prevDeps || !nextDeps)
return prevDeps === nextDeps;
if (prevDeps.length !== nextDeps.length)
return false;
return prevDeps.every((prev, index) => {
const next = nextDeps[index];
if (typeof prev === 'object' && prev !== null) {
return (0, lodash_isequal_1.default)(prev, next);
}
return Object.is(prev, next);
});
}
/**
* Analyzes dependencies to categorize them and detect potential issues
*/
function analyzeDeps(deps) {
const analysis = {
primitives: [],
objects: [],
functions: [],
potentiallyMissing: [],
redundant: []
};
deps.forEach((dep, index) => {
const type = typeof dep;
switch (type) {
case 'string':
case 'number':
case 'boolean':
case 'undefined':
analysis.primitives.push(dep);
break;
case 'function':
analysis.functions.push(dep);
// More precise anonymous function detection
const fnString = dep.toString().trim();
// Check if it's truly an anonymous function by examining the source
const isAnonymous =
// Arrow functions are generally considered anonymous in this context
fnString.includes('=>') ||
// Function expressions without names
/^function\s*\(/.test(fnString) ||
// Functions with empty or 'anonymous' names
!dep.name ||
dep.name === 'anonymous';
analysis.potentiallyMissing.push(isAnonymous ? `Anonymous function at index ${index}` : `Named function at index ${index}`);
break;
case 'object':
if (dep === null) {
analysis.primitives.push(dep);
}
else if (Array.isArray(dep)) {
analysis.objects.push(dep);
analysis.potentiallyMissing.push(`Array at index ${index}`);
}
else {
analysis.objects.push(dep);
analysis.potentiallyMissing.push(`Object at index ${index}`);
}
break;
}
});
return analysis;
}
/**
* Generates a warning message for dependency analysis
*/
function generateDependencyWarning(analysis, effectId) {
const warnings = [];
if (analysis.potentiallyMissing.length > 0) {
warnings.push(`Potentially unstable dependencies detected:\n${analysis.potentiallyMissing
.map(msg => ` - ${msg}`)
.join('\n')}`);
}
if (analysis.functions.length > 0) {
warnings.push(`Function dependencies detected (${analysis.functions.length}). Consider wrapping with useCallback.`);
}
if (analysis.objects.length > 0) {
warnings.push(`Object/Array dependencies detected (${analysis.objects.length}). Consider wrapping with useMemo or enable deepCompare.`);
}
if (warnings.length === 0)
return null;
const prefix = effectId ? `[useSmartEffect:${effectId}]` : '[useSmartEffect]';
return `${prefix} Dependency Analysis:\n${warnings.join('\n\n')}`;
}
/**
* Logs dependency changes for debugging
*/
function logDependencyChanges(prevDeps, nextDeps, effectId) {
if (!prevDeps || !nextDeps)
return;
const changes = nextDeps
.map((next, index) => {
const prev = prevDeps[index];
const changed = !Object.is(prev, next) && !(0, lodash_isequal_1.default)(prev, next);
return { index, prev, next, changed };
})
.filter(c => c.changed);
if (changes.length > 0) {
const prefix = effectId ? `[useSmartEffect:${effectId}]` : '[useSmartEffect]';
console.group(`${prefix} Dependencies Changed`);
changes.forEach(({ index, prev, next }) => console.log(`Index ${index}:`, { prev, next }));
console.groupEnd();
}
}
/**
* Creates a unique identifier for an effect
*/
function createEffectId() {
return `effect_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
/**
* Formats dependency values for display
*/
function formatDependencyValue(value) {
if (value === null)
return 'null';
if (value === undefined)
return 'undefined';
if (typeof value === 'function')
return `[Function ${value.name || 'anonymous'}]`;
if (typeof value === 'object') {
if (Array.isArray(value))
return `Array(${value.length})`;
return `Object(${Object.keys(value).length} keys)`;
}
return String(value);
}
;