Fastest deep equal comparison for React with optimizations for React elements and circular reference handling
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
React Fast Compare provides the fastest deep equal comparison for React applications, with optimizations for React elements and built-in circular reference handling. It offers a single unified entry point that handles React-specific circular references like React elements, includes try/catch guardrails for stack overflows, and supports all JavaScript data types.
npm install react-fast-compare or yarn add react-fast-compareconst isEqual = require('react-fast-compare');For ES modules/TypeScript:
import isEqual = require('react-fast-compare');const isEqual = require('react-fast-compare');
// General deep comparison
console.log(isEqual({ foo: 'bar' }, { foo: 'bar' })); // true
console.log(isEqual([1, 2, 3], [1, 2, 3])); // true
// React.memo usage
const MemoComponent = React.memo(ExpensiveComponent, isEqual);
// shouldComponentUpdate usage
class Component extends React.Component {
shouldComponentUpdate(nextProps) {
return !isEqual(this.props, nextProps);
}
}
// React-Redux useSelector usage
const value = useSelector(selector, isEqual);Performs deep equality comparison optimized for React applications with circular reference handling.
/**
* Compare two values for deep equality with React-specific optimizations
* @param a - First value to compare
* @param b - Second value to compare
* @returns true if values are deeply equal, false otherwise
*/
function isEqual<A = any, B = any>(a: A, b: B): boolean;Supported Data Types:
React-Specific Features:
_owner, __v, __o properties in React/Preact elements to avoid infinite loopsError Handling:
false and logs "react-fast-compare cannot handle circular refs" warningfalsePerformance Characteristics:
Usage Examples:
// Basic object comparison
isEqual({ name: 'Alice', age: 25 }, { name: 'Alice', age: 25 }); // true
isEqual({ name: 'Alice' }, { name: 'Bob' }); // false
// Array comparison
isEqual([1, 2, [3, 4]], [1, 2, [3, 4]]); // true
isEqual([1, 2, 3], [1, 2, 4]); // false
// Date comparison
isEqual(new Date('2023-01-01'), new Date('2023-01-01')); // true
isEqual(new Date('2023-01-01'), new Date('2023-01-02')); // false
// RegExp comparison
isEqual(/abc/gi, /abc/gi); // true
isEqual(/abc/g, /abc/i); // false
// Map comparison
const map1 = new Map([['a', 1], ['b', 2]]);
const map2 = new Map([['a', 1], ['b', 2]]);
isEqual(map1, map2); // true
// Set comparison
const set1 = new Set([1, 2, 3]);
const set2 = new Set([1, 2, 3]);
isEqual(set1, set2); // true
// React element comparison (skips circular references)
const element1 = React.createElement('div', { id: 'test' }, 'Hello');
const element2 = React.createElement('div', { id: 'test' }, 'Hello');
isEqual(element1, element2); // true (ignores _owner and other circular props)
// Typed array comparison
const arr1 = new Uint8Array([1, 2, 3]);
const arr2 = new Uint8Array([1, 2, 3]);
isEqual(arr1, arr2); // trueIntegration Examples:
// React.memo with custom comparison
interface Props {
data: ComplexObject;
config: ConfigObject;
}
const OptimizedComponent = React.memo<Props>(({ data, config }) => {
// Expensive component logic
return <div>{/* Complex UI */}</div>;
}, isEqual);
// Class component shouldComponentUpdate
class ExpensiveList extends React.Component<ListProps> {
shouldComponentUpdate(nextProps: ListProps) {
// Only re-render if props actually changed
return !isEqual(this.props, nextProps);
}
render() {
return (
<ul>
{this.props.items.map(item =>
<li key={item.id}>{item.name}</li>
)}
</ul>
);
}
}
// React-Redux useSelector with equality check
const selectUserData = (state: RootState) => ({
user: state.user,
preferences: state.preferences,
settings: state.settings
});
function UserProfile() {
// Prevents unnecessary re-renders when reference changes but content is same
const userData = useSelector(selectUserData, isEqual);
return <div>{userData.user.name}</div>;
}