A React compatibility layer for Preact
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Development and performance utilities including pure render mixins and performance measurement tools.
Performance optimization mixin that implements shallow comparison for shouldComponentUpdate.
/**
* Mixin that provides shallow comparison for shouldComponentUpdate
*/
const PureRenderMixin = {
/**
* Shallow comparison implementation for props and state
* @param {object} nextProps - Next props object
* @param {object} nextState - Next state object
* @returns {boolean} Whether component should update
*/
shouldComponentUpdate(nextProps, nextState);
};Usage Examples:
import { PureRenderMixin } from 'preact-compat';
// or
import PureRenderMixin from 'preact-compat/lib/ReactComponentWithPureRenderMixin';
// With createClass
const OptimizedComponent = createClass({
mixins: [PureRenderMixin],
render() {
return (
<div>
<h1>{this.props.title}</h1>
<p>{this.props.content}</p>
</div>
);
}
});
// Manual implementation in ES6 class
class ManualPureComponent extends Component {
shouldComponentUpdate(nextProps, nextState) {
return PureRenderMixin.shouldComponentUpdate.call(
this,
nextProps,
nextState
);
}
render() {
return <div>{this.props.data}</div>;
}
}
// Or use PureComponent (recommended)
class BetterPureComponent extends PureComponent {
render() {
return <div>{this.props.data}</div>;
}
}Development tools for measuring component performance (no-op implementations in production).
/**
* Performance measurement utilities (no-op in Preact)
*/
const ReactPerf = {
/**
* Start performance measurement
*/
start(): void;
/**
* Stop performance measurement
*/
stop(): void;
/**
* Check if profiling is currently running
* @returns {boolean} Always false in Preact
*/
isRunning(): boolean;
/**
* Get last measurement data
* @returns {Array} Empty array in Preact
*/
getLastMeasurements(): Array<any>;
/**
* Get exclusive time measurements
* @param {Array} measurements - Measurement data
* @returns {Array} Empty array in Preact
*/
getExclusive(measurements): Array<any>;
/**
* Get inclusive time measurements
* @param {Array} measurements - Measurement data
* @returns {Array} Empty array in Preact
*/
getInclusive(measurements): Array<any>;
/**
* Get wasted time measurements
* @param {Array} measurements - Measurement data
* @returns {Array} Empty array in Preact
*/
getWasted(measurements): Array<any>;
/**
* Get operation measurements
* @param {Array} measurements - Measurement data
* @returns {Array} Empty array in Preact
*/
getOperations(measurements): Array<any>;
/**
* Print exclusive time measurements (no-op)
* @param {Array} measurements - Measurement data
*/
printExclusive(measurements): void;
/**
* Print inclusive time measurements (no-op)
* @param {Array} measurements - Measurement data
*/
printInclusive(measurements): void;
/**
* Print wasted time measurements (no-op)
* @param {Array} measurements - Measurement data
*/
printWasted(measurements): void;
/**
* Print operation measurements (no-op)
* @param {Array} measurements - Measurement data
*/
printOperations(measurements): void;
};Usage Examples:
import { ReactPerf } from 'preact-compat';
// or
import ReactPerf from 'preact-compat/lib/ReactPerf';
// Performance measurement (no-op in Preact, but compatible API)
if (process.env.NODE_ENV === 'development') {
// Start profiling
ReactPerf.start();
// ... perform operations to measure
// Stop profiling and collect results
ReactPerf.stop();
const measurements = ReactPerf.getLastMeasurements();
// Print different types of measurements
ReactPerf.printInclusive(measurements);
ReactPerf.printExclusive(measurements);
ReactPerf.printWasted(measurements);
ReactPerf.printOperations(measurements);
// Check if profiling is running
console.log('Profiling active:', ReactPerf.isRunning());
}Utilities for handling CSS transition and animation events.
/**
* Add event listener for transition/animation end events
* @param {Element} node - DOM element
* @param {function} eventListener - Event handler function
*/
function addEndEventListener(node, eventListener);
/**
* Remove event listener for transition/animation end events
* @param {Element} node - DOM element
* @param {function} eventListener - Event handler function
*/
function removeEndEventListener(node, eventListener);Usage Examples:
import {
addEndEventListener,
removeEndEventListener
} from 'preact-compat/lib/ReactTransitionEvents';
class AnimatedComponent extends Component {
componentDidMount() {
const element = this.refs.animatedElement;
// Add transition end listener
this.handleTransitionEnd = (event) => {
console.log('Transition completed:', event);
this.setState({ animationComplete: true });
};
addEndEventListener(element, this.handleTransitionEnd);
}
componentWillUnmount() {
const element = this.refs.animatedElement;
removeEndEventListener(element, this.handleTransitionEnd);
}
startAnimation = () => {
const element = this.refs.animatedElement;
element.classList.add('animate');
};
render() {
return (
<div>
<div
ref="animatedElement"
className="animated-box"
style={{
width: '100px',
height: '100px',
background: 'blue',
transition: 'transform 0.3s ease'
}}
/>
<button onClick={this.startAnimation}>
Start Animation
</button>
</div>
);
}
}/**
* Unmount component from DOM node
* @param {Element} container - DOM container element
* @returns {boolean} True if component was unmounted
*/
function unmountComponentAtNode(container);Usage Examples:
import { unmountComponentAtNode } from 'preact-compat';
// or
import { unmountComponentAtNode } from 'preact-compat/lib/ReactMount';
// Clean up component
const container = document.getElementById('app');
const wasUnmounted = unmountComponentAtNode(container);
if (wasUnmounted) {
console.log('Component successfully unmounted');
} else {
console.log('No component was mounted in container');
}import { PureComponent } from 'preact-compat';
// Use PureComponent for components with simple props
class OptimizedListItem extends PureComponent {
render() {
const { item, onSelect } = this.props;
return (
<div onClick={() => onSelect(item.id)}>
{item.name}
</div>
);
}
}
// Memoize expensive computations outside render
class ExpensiveComponent extends PureComponent {
constructor(props) {
super(props);
this.memoizedResult = null;
this.lastInput = null;
}
expensiveCalculation = (input) => {
if (this.lastInput === input && this.memoizedResult !== null) {
return this.memoizedResult;
}
// Simulate expensive calculation
const result = input.split('').reverse().join('').repeat(1000);
this.memoizedResult = result;
this.lastInput = input;
return result;
};
render() {
const result = this.expensiveCalculation(this.props.input);
return <div>{result.substring(0, 100)}...</div>;
}
}
// Use keys for dynamic lists
class DynamicList extends Component {
render() {
return (
<div>
{this.props.items.map(item => (
<OptimizedListItem
key={item.id} // Important for performance
item={item}
onSelect={this.props.onSelect}
/>
))}
</div>
);
}
}// Main imports
import { PureRenderMixin, ReactPerf } from 'preact-compat';
// Specific library imports
import PureRenderMixin from 'preact-compat/lib/ReactComponentWithPureRenderMixin';
import ReactPerf from 'preact-compat/lib/ReactPerf';
import { addEndEventListener, removeEndEventListener } from 'preact-compat/lib/ReactTransitionEvents';
import { unmountComponentAtNode } from 'preact-compat/lib/ReactMount';
// CommonJS
const { PureRenderMixin, ReactPerf } = require('preact-compat');interface PureRenderMixinType {
shouldComponentUpdate(nextProps: object, nextState: object): boolean;
}
interface ReactPerfType {
start(): void;
stop(): void;
isRunning(): boolean;
getLastMeasurements(): Array<any>;
getExclusive(measurements: Array<any>): Array<any>;
getInclusive(measurements: Array<any>): Array<any>;
getWasted(measurements: Array<any>): Array<any>;
getOperations(measurements: Array<any>): Array<any>;
printExclusive(measurements: Array<any>): void;
printInclusive(measurements: Array<any>): void;
printWasted(measurements: Array<any>): void;
printOperations(measurements: Array<any>): void;
}
type TransitionEventListener = (event: TransitionEvent | AnimationEvent) => void;
interface TransitionEventUtils {
addEndEventListener(node: Element, eventListener: TransitionEventListener): void;
removeEndEventListener(node: Element, eventListener: TransitionEventListener): void;
}Install with Tessl CLI
npx tessl i tessl/npm-preact-compat