React Shallow Equal Utilities
The `react-shallow-equal` package provides specific utility functions for performing efficient shallow equality checks within React and React Native environments. It exposes `propsEqual`, `elementsEqual`, and `stylesEqual` functions, designed primarily to optimize component re-renders through `shouldComponentUpdate` by comparing component properties, React elements, and style objects. The current stable version is 0.1.1. However, the package has not seen updates in approximately 8 years, making its release cadence non-existent. It is a fork of `lelandrichardson/shallow-element-equals` and utilizes concepts from `lelandrichardson/style-equal`. Its key differentiator was its specialized focus on React-specific shallow comparisons, but modern React development largely utilizes `React.PureComponent` and `React.memo` for similar optimizations.
Common errors
-
TypeError: Cannot destructure property 'propsEqual' of ... as it is undefined.
cause Attempting to import `propsEqual` using CommonJS `require` syntax on a module that might be primarily ES Module (ESM) or packaged incorrectly for CJS, or the package path is incorrect.fixEnsure you are using the correct ES Module import syntax: `import { propsEqual } from 'react-shallow-equal';`. If using CommonJS, verify the package exports named properties correctly or adjust your build system (e.g., Babel/Webpack) to handle ESM imports. -
Component not re-rendering when nested data changes.
cause Shallow equality (used by `propsEqual` and `PureComponent`/`React.memo`) only compares references for non-primitive values. If a nested object's contents change but its reference remains the same, the shallow comparison will return true (meaning 'no change'), and the component will not re-render.fixAdopt immutable data patterns. When updating state or props that contain nested objects, always create new object references for the parent object(s) up to the root if any nested property changes. For example, use spread syntax (`{...prevObj, nested: {...prevObj.nested, prop: newValue}}`) to create new objects.
Warnings
- breaking This package is considered abandoned, having not been updated in over 8 years. It is highly recommended to use built-in React features like `React.PureComponent` for class components or `React.memo` with `useCallback`/`useMemo` for functional components, which provide similar and often more optimized shallow comparison behavior.
- gotcha Shallow equality checks only compare the values of properties at the first level of an object or array. For nested objects or arrays, it only compares their references, not their contents. If a nested object changes its internal values but maintains the same reference, `propsEqual` (and `PureComponent`/`React.memo`) will not detect a change and prevent re-rendering, potentially leading to stale UI.
- deprecated The functionality provided by `react-shallow-equal` has been largely superseded by native React features. `React.PureComponent` (introduced in React 15.3.0) and `React.memo` (introduced in React 16.6.0) offer built-in shallow comparison for `props` and `state` out of the box.
Install
-
npm install react-shallow-equal -
yarn add react-shallow-equal -
pnpm add react-shallow-equal
Imports
- propsEqual
const propsEqual = require('react-shallow-equal').propsEqual;import { propsEqual } from 'react-shallow-equal'; - elementsEqual
import elementsEqual from 'react-shallow-equal';
import { elementsEqual } from 'react-shallow-equal'; - stylesEqual
const stylesEqual = require('react-shallow-equal');import { stylesEqual } from 'react-shallow-equal';
Quickstart
import React, { PureComponent } from 'react';
import { propsEqual } from 'react-shallow-equal';
class MyOptimizedComponent extends PureComponent {
// In a traditional React component (not PureComponent), you would implement this:
// shouldComponentUpdate(nextProps, nextState) {
// // Only re-render if props are NOT shallowly equal or state has changed
// return !propsEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);
// }
render() {
// Simulate rendering based on props, e.g., from an API call or parent component
const { data, isActive, onAction } = this.props;
return (
<div>
<h2>Item: {data.name}</h2>
<p>Status: {isActive ? 'Active' : 'Inactive'}</p>
<button onClick={onAction}>Perform Action</button>
<p>Last updated: {new Date().toLocaleTimeString()}</p>
</div>
);
}
}
// Example of usage in a parent component
function App() {
const [count, setCount] = React.useState(0);
const [itemData, setItemData] = React.useState({ id: 1, name: 'Example Item' });
const handleAction = React.useCallback(() => {
console.log('Action performed!');
setCount(prev => prev + 1);
}, []);
// This prop object will be new on every render, but MyOptimizedComponent
// would only re-render if data.name or isActive actually changed values
// if using propsEqual inside shouldComponentUpdate.
// With PureComponent, it handles this automatically with its own shallow comparison.
const componentProps = {
data: itemData,
isActive: count % 2 === 0,
onAction: handleAction,
extraProp: count // This will cause PureComponent to re-render every time it changes
};
React.useEffect(() => {
const interval = setInterval(() => {
// This update will cause App to re-render, creating new componentProps
// PureComponent will then shallow compare these new props.
setCount(c => c + 1);
}, 2000);
return () => clearInterval(interval);
}, []);
return (
<div>
<h1>Parent App Render Count: {count}</h1>
<MyOptimizedComponent {...componentProps} />
</div>
);
}