Webpack plugin to enable React Fast Refresh (Hot Reloading) for React components during development
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
React Refresh runtime utilities that handle module boundary detection, component registration, and hot reload coordination at runtime.
Schedules a React Refresh update to be processed during the next update cycle.
/**
* Enqueues a React component update for processing
* @returns {void}
*/
function enqueueUpdate(): void;Usage Example:
const RefreshUtils = require('@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils');
// Trigger a refresh update
RefreshUtils.enqueueUpdate();Executes the React Refresh runtime, processing any pending updates and refreshing affected components.
/**
* Executes the React Refresh runtime to process pending updates
* @returns {void}
*/
function executeRuntime(): void;Usage Example:
const RefreshUtils = require('@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils');
// Execute runtime after module updates
RefreshUtils.executeRuntime();Retrieves the current exports of a webpack module by its ID.
/**
* Gets the current exports of a webpack module
* @param {string} moduleId - The webpack module identifier
* @returns {any} - The module's current exports
*/
function getModuleExports(moduleId: string): any;Usage Example:
const RefreshUtils = require('@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils');
// Get module exports for inspection
const moduleExports = RefreshUtils.getModuleExports('./src/components/Button.jsx');
console.log('Button component exports:', moduleExports);Determines whether a module's exports constitute a valid React Refresh boundary (i.e., contains React components).
/**
* Checks if module exports represent a React Refresh boundary
* @param {any} moduleExports - The exports to check
* @returns {boolean} - True if exports form a valid refresh boundary
*/
function isReactRefreshBoundary(moduleExports: any): boolean;Usage Example:
const RefreshUtils = require('@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils');
// Check if a module is a React component boundary
const ButtonExports = require('./Button.jsx');
const isRefreshable = RefreshUtils.isReactRefreshBoundary(ButtonExports);
console.log('Button is refreshable:', isRefreshable);
// Non-React modules return false
const utilsExports = require('./utils.js');
const isUtilsRefreshable = RefreshUtils.isReactRefreshBoundary(utilsExports);
console.log('Utils is refreshable:', isUtilsRefreshable); // falseRegisters a module's exports with the React Refresh runtime for hot reloading.
/**
* Registers module exports with React Refresh runtime
* @param {string} moduleId - The webpack module identifier
* @param {any} moduleExports - The module's exports to register
* @returns {void}
*/
function registerExportsForReactRefresh(moduleId: string, moduleExports: any): void;Usage Example:
const RefreshUtils = require('@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils');
// Register React component for refresh
const MyComponent = require('./MyComponent.jsx');
RefreshUtils.registerExportsForReactRefresh('./src/MyComponent.jsx', MyComponent);
// Typically used in module hot accept callbacks
if (module.hot) {
module.hot.accept('./MyComponent.jsx', () => {
const UpdatedComponent = require('./MyComponent.jsx');
RefreshUtils.registerExportsForReactRefresh('./src/MyComponent.jsx', UpdatedComponent);
RefreshUtils.enqueueUpdate();
});
}Implement custom hot reload logic using runtime utilities:
const RefreshUtils = require('@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils');
function handleModuleUpdate(moduleId, newExports) {
// Check if module can be hot reloaded
if (RefreshUtils.isReactRefreshBoundary(newExports)) {
console.log(`Module ${moduleId} is refreshable`);
// Register updated exports
RefreshUtils.registerExportsForReactRefresh(moduleId, newExports);
// Trigger refresh
RefreshUtils.enqueueUpdate();
RefreshUtils.executeRuntime();
} else {
console.log(`Module ${moduleId} requires full reload`);
window.location.reload();
}
}
// Usage in webpack HMR accept callback
if (module.hot) {
module.hot.accept(['./ComponentA.jsx', './ComponentB.jsx'], (updatedDependencies) => {
updatedDependencies.forEach(moduleId => {
const newExports = RefreshUtils.getModuleExports(moduleId);
handleModuleUpdate(moduleId, newExports);
});
});
}Detect and categorize different types of module exports:
const RefreshUtils = require('@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils');
function categorizeModule(moduleExports, moduleId) {
if (RefreshUtils.isReactRefreshBoundary(moduleExports)) {
return {
type: 'react-component',
refreshable: true,
moduleId,
exports: moduleExports
};
}
// Check for higher-order components or hooks
if (typeof moduleExports === 'function' && moduleExports.name.startsWith('use')) {
return {
type: 'react-hook',
refreshable: true, // Hooks can be refreshed
moduleId,
exports: moduleExports
};
}
return {
type: 'utility',
refreshable: false,
moduleId,
exports: moduleExports
};
}
// Categorize current module
const moduleInfo = categorizeModule(module.exports, module.id);
console.log('Module info:', moduleInfo);
if (moduleInfo.refreshable) {
RefreshUtils.registerExportsForReactRefresh(moduleInfo.moduleId, moduleInfo.exports);
}Process multiple module updates efficiently:
const RefreshUtils = require('@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils');
class RefreshBatchProcessor {
constructor() {
this.pendingUpdates = new Set();
this.processingTimeout = null;
}
addUpdate(moduleId, moduleExports) {
// Register the module
if (RefreshUtils.isReactRefreshBoundary(moduleExports)) {
RefreshUtils.registerExportsForReactRefresh(moduleId, moduleExports);
this.pendingUpdates.add(moduleId);
// Schedule batch processing
this.scheduleBatchProcess();
}
}
scheduleBatchProcess() {
if (this.processingTimeout) {
clearTimeout(this.processingTimeout);
}
this.processingTimeout = setTimeout(() => {
this.processBatch();
}, 10); // Small delay to batch multiple updates
}
processBatch() {
if (this.pendingUpdates.size > 0) {
console.log(`Processing ${this.pendingUpdates.size} pending updates`);
// Single enqueue and execute for all updates
RefreshUtils.enqueueUpdate();
RefreshUtils.executeRuntime();
this.pendingUpdates.clear();
}
this.processingTimeout = null;
}
}
// Global batch processor
const batchProcessor = new RefreshBatchProcessor();
// Use in HMR callbacks
if (module.hot) {
module.hot.accept(['./ComponentA.jsx', './ComponentB.jsx'], () => {
const ComponentA = RefreshUtils.getModuleExports('./ComponentA.jsx');
const ComponentB = RefreshUtils.getModuleExports('./ComponentB.jsx');
batchProcessor.addUpdate('./ComponentA.jsx', ComponentA);
batchProcessor.addUpdate('./ComponentB.jsx', ComponentB);
});
}These utilities are typically used by the webpack loader and runtime modules automatically, but can be used directly for advanced customization:
// Custom loader or plugin integration
const RefreshUtils = require('@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils');
// In a custom webpack loader
module.exports = function customReactLoader(source) {
if (this.mode === 'development') {
// Add runtime registration code
const refreshCode = `
const RefreshUtils = require('@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils');
if (RefreshUtils.isReactRefreshBoundary(module.exports)) {
RefreshUtils.registerExportsForReactRefresh(module.id, module.exports);
if (module.hot) {
module.hot.accept(() => {
RefreshUtils.enqueueUpdate();
RefreshUtils.executeRuntime();
});
}
}
`;
return source + '\n\n' + refreshCode;
}
return source;
};Install with Tessl CLI
npx tessl i tessl/npm-pmmmwh--react-refresh-webpack-plugin