React Mixin Utility for ES6 Classes
raw JSON →react-mixin is a utility library designed to enable the use of traditional React mixins with ES6/TypeScript classes, addressing the absence of built-in support for this pattern in React's class-based components. While React itself has deprecated mixins in favor of Higher-Order Components and Hooks, this package serves as a specific migration path for legacy codebases that must integrate existing mixins with modern class syntax. The current stable version is 5.0.0, which notably adapted to React's `UNSAFE_` lifecycle methods. The library provides mechanisms to apply mixins to class prototypes for instance methods and to the classes themselves for static properties like `defaultProps` and `propTypes`. It differentiates itself by offering clear error handling for conflicting method names (instead of silent overwrites) and supporting an optional decorator syntax, allowing developers to manage mixins in a structured way within their class definitions. However, it explicitly advocates for avoiding mixins in new development.
Common errors
error TypeError: Cannot read properties of undefined (reading 'setState') OR 'this' is undefined in mixin method. ↓
this.myMethod = this.myMethod.bind(this); or define the method using an arrow function for class fields if supported by your build setup: myMethod = () => { /* ... */ }. error Warning: componentWillMount has been renamed, and is not recommended for use. OR Mixin lifecycle method (e.g., `componentWillMount`) is not firing in React v16.3+. ↓
react-mixin to ^5.0.0 and either rename the affected mixin methods (e.g., componentWillMount to UNSAFE_componentWillMount) or wrap the mixin with toUnsafe() before applying it: reactMixin(MyClass.prototype, toUnsafe(myMixin));. error TypeError: Cannot read properties of undefined (reading 'defaultProps') OR Static properties from a mixin like `getDefaultProps` or `propTypes` are not applied to the component. ↓
reactMixin.onClass(MyClass, myMixin) to apply mixins that define static or class-level properties. This ensures the properties are correctly merged with the class definition. Warnings
deprecated React mixins are a deprecated pattern. The `react-mixin` library itself is primarily intended as a migration path for legacy code using ES6 classes with mixins. New development should strongly prefer Higher-Order Components (HOCs) or Hooks for code reuse and shared logic. ↓
breaking Version 5.0 of `react-mixin` aligns with React's deprecation of `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. Mixins defining these methods will no longer function correctly and must be renamed to their `UNSAFE_` counterparts (e.g., `UNSAFE_componentWillMount`) or processed with the `react-mixin/toUnsafe` utility. ↓
gotcha `react-mixin` does not automatically bind `this` context for methods within mixins or ES6 class methods, unlike `React.createClass`. Method callbacks (e.g., event handlers) will lose `this` context if not explicitly bound. ↓
gotcha When using `reactMixin.decorate(mixin)` as an ES7 decorator, it performs prototypical inheritance, which means it returns a *new* class rather than mutating the original class in place. This can have implications for class identity checks or direct references to the original class. ↓
gotcha `react-mixin` provides errors instead of silently overwriting conflicting methods from multiple mixins, with the exception of specific whitelisted methods (like `getDefaultProps` and `getInitialState`) which are explicitly designed for merging. ↓
Install
npm install react-mixin yarn add react-mixin pnpm add react-mixin Imports
- reactMixin wrong
const reactMixin = require('react-mixin'); // Correct for CJS, but prefer ESM for modern apps.correctimport reactMixin from 'react-mixin'; - reactMixin.onClass wrong
const reactMixin = require('react-mixin'); reactMixin(MyClass, myMixin); // 'onClass' method is specifically for static properties.correctimport reactMixin from 'react-mixin'; reactMixin.onClass(MyClass, myMixin); - toUnsafe wrong
import { toUnsafe } from 'react-mixin'; // 'toUnsafe' is a separate export, not a named export from the main module.correctimport toUnsafe from 'react-mixin/toUnsafe'; - reactMixin.decorate
import reactMixin from 'react-mixin'; @reactMixin.decorate(myMixin) class MyClass {}
Quickstart
import React from 'react';
import reactMixin from 'react-mixin';
import toUnsafe from 'react-mixin/toUnsafe';
// Define a simple React Component
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
// Autobinding is not provided by react-mixin; explicit binding is necessary.
this.handleClick = this.handleClick.bind(this);
}
// Example method that might be part of or overridden by a mixin
handleClick() {
this.setState(prevState => ({ count: prevState.count + 1 }));
}
render() {
return (
<div>
<h1>Count: {this.state.count}</h1>
<button onClick={this.handleClick}>Increment</button>
<p>Props: {JSON.stringify(this.props)}</p>
</div>
);
}
}
// Define example mixins
const MyStateMixin = {
getInitialState() { // This method will be merged
return { mixedData: 'initial value' };
},
componentDidMount() {
console.log('MyStateMixin mounted!');
},
// Version 5.0+ requires UNSAFE_ prefix for these lifecycle methods
UNSAFE_componentWillMount() {
console.log('MyStateMixin UNSAFE_componentWillMount!');
}
};
const MyPropsMixin = {
// This will be merged at the class level using .onClass
getDefaultProps() {
return { defaultPropFromMixin: 'default' };
},
componentDidUpdate(prevProps, prevState) {
console.log('MyPropsMixin updated!');
}
};
// --- Apply mixins to the component's prototype ---
reactMixin(MyComponent.prototype, MyStateMixin);
// --- Apply mixins with UNSAFE_ method conversion for v5+ ---
// If MyPropsMixin had componentWillMount, we'd use toUnsafe on it.
// For demonstration, let's assume it did and apply it through toUnsafe for clarity.
const fixedPropsMixin = toUnsafe(MyPropsMixin);
reactMixin(MyComponent.prototype, fixedPropsMixin);
// --- Apply mixins to the class itself for static properties (e.g., getDefaultProps) ---
reactMixin.onClass(MyComponent, MyPropsMixin); // Note: use the original mixin here for getDefaultProps merging.
// --- Example using the decorator syntax (requires Babel with decorator support) ---
// @reactMixin.decorate(MyStateMixin)
// class DecoratedComponent extends React.Component {
// render() { return <div>Decorated Component!</div>; }
// }
// Simulate instantiation to show applied props/state (in a real app, you'd render this to DOM)
console.log("Component created with mixins applied. Check console for lifecycle logs.");
const instance = new MyComponent({ customInput: 'test' });
console.log('Initial component state:', instance.state); // Should include mixedData
console.log('Initial component props (merged with defaults):', instance.props); // Should include defaultPropFromMixin