= >(
Comp: C
) => C &
@@ -25,8 +20,6 @@ const createWithTheme = >(
) =>
function withTheme(Comp: *) {
class ThemedComponent extends React.Component<*> {
- static displayName = `withTheme(${Comp.displayName || Comp.name})`;
-
_previous: ?{ a: T, b: ?S, result: T };
_merge = (a: T, b: ?S) => {
@@ -36,39 +29,29 @@ const createWithTheme = >(
return previous.result;
}
- const result = a && b ? deepmerge(a, b) : a || b;
+ const result = a && b && a !== b ? deepmerge(a, b) : a || b;
this._previous = { a, b, result };
return result;
};
- _root: any;
-
render() {
+ const { _reactThemeProviderForwardedRef, ...rest } = this.props;
+
return (
{theme => {
- const merged = this._merge(theme, this.props.theme);
-
- let element;
- if (isClassComponent(Comp)) {
- // Only add refs for class components as function components don't support them
- // It's needed to support use cases which need access to the underlying node
- element = (
- {
- this._root = c;
- }}
- theme={merged}
- />
- );
- } else {
- element = ;
- }
-
- if (merged !== this.props.theme) {
+ const merged = this._merge(theme, rest.theme);
+ const element = (
+
+ );
+
+ if (rest.theme && merged !== rest.theme) {
// If a theme prop was passed, expose it to the children
return {element};
}
@@ -80,23 +63,15 @@ const createWithTheme = >(
}
}
- if (isClassComponent(Comp)) {
- // getWrappedInstance is exposed by some HOCs like react-redux's connect
- // Use it to get the ref to the underlying element
- // Also expose it to access the underlying element after wrapping
- // $FlowFixMe
- ThemedComponent.prototype.getWrappedInstance = function getWrappedInstance() {
- return this._root && this._root.getWrappedInstance
- ? this._root.getWrappedInstance()
- : this._root;
- };
+ const ResultComponent = React.forwardRef((props, ref) => (
+
+ ));
- ThemedComponent = copyRefs(ThemedComponent, Comp);
- }
+ ResultComponent.displayName = `withTheme(${Comp.displayName || Comp.name})`;
- hoistNonReactStatics(ThemedComponent, Comp);
+ hoistNonReactStatics(ResultComponent, Comp);
- return (ThemedComponent: any);
+ return (ResultComponent: any);
};
export default createWithTheme;
diff --git a/src/utils.js b/src/utils.js
deleted file mode 100644
index 302f550..0000000
--- a/src/utils.js
+++ /dev/null
@@ -1,81 +0,0 @@
-/* @flow */
-
-import * as React from 'react';
-
-const REACT_METHODS = [
- 'autobind',
- 'childContextTypes',
- 'componentDidMount',
- 'componentDidUpdate',
- 'componentWillMount',
- 'componentWillReceiveProps',
- 'componentWillUnmount',
- 'componentWillUpdate',
- 'contextTypes',
- 'displayName',
- 'getChildContext',
- 'getDefaultProps',
- 'getDOMNode',
- 'getInitialState',
- 'mixins',
- 'propTypes',
- 'render',
- 'replaceProps',
- 'setProps',
- 'shouldComponentUpdate',
- 'statics',
- 'updateComponent',
-];
-
-export function copyRefs(
- TargetComponent: React.ComponentType,
- SourceComponent: React.ComponentType
-): React.ComponentType {
- // $FlowFixMe
- if (!SourceComponent.prototype) {
- return TargetComponent;
- }
-
- // $FlowFixMe
- Object.getOwnPropertyNames(SourceComponent.prototype)
- .filter(
- prop =>
- !(
- REACT_METHODS.includes(prop) || // React specific methods and properties
- prop in React.Component.prototype || // Properties from React's prototype such as `setState`
- // $FlowFixMe
- prop in TargetComponent.prototype || // Properties from enhanced component's prototype
- // Private methods
- prop.startsWith('_')
- )
- )
- .forEach(prop => {
- // $FlowFixMe
- if (typeof SourceComponent.prototype[prop] === 'function') {
- /* eslint-disable func-names, no-param-reassign */
- // $FlowFixMe
- TargetComponent.prototype[prop] = function(...args) {
- // Make sure the function is called with correct context
- // $FlowFixMe
- return SourceComponent.prototype[prop].apply(
- this.getWrappedInstance(),
- args
- );
- };
- } else {
- // Copy properties as getters and setters
- // This make sure dynamic properties always stay up-to-date
- // $FlowFixMe
- Object.defineProperty(TargetComponent.prototype, prop, {
- get() {
- return this.getWrappedInstance()[prop];
- },
- set(value) {
- this.getWrappedInstance()[prop] = value;
- },
- });
- }
- });
-
- return TargetComponent;
-}