Skip to content

Commit 2bafaf9

Browse files
committed
fix: copy public methods from component in HOC. fixes callstack#254
1 parent 87427fe commit 2bafaf9

File tree

1 file changed

+65
-11
lines changed

1 file changed

+65
-11
lines changed

src/core/withTheme.js

+65-11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,31 @@ import merge from 'deepmerge';
66
import ThemeProvider, { ThemeContext } from './ThemeProvider';
77
import type { Theme } from '../types';
88

9+
const REACT_METHODS = [
10+
'autobind',
11+
'childContextTypes',
12+
'componentDidMount',
13+
'componentDidUpdate',
14+
'componentWillMount',
15+
'componentWillReceiveProps',
16+
'componentWillUnmount',
17+
'componentWillUpdate',
18+
'contextTypes',
19+
'displayName',
20+
'getChildContext',
21+
'getDefaultProps',
22+
'getDOMNode',
23+
'getInitialState',
24+
'mixins',
25+
'propTypes',
26+
'render',
27+
'replaceProps',
28+
'setProps',
29+
'shouldComponentUpdate',
30+
'statics',
31+
'updateComponent',
32+
];
33+
934
const isClassComponent = (Component: Function) => !!Component.prototype.render;
1035

1136
export default function withTheme<Props: {}>(
@@ -81,17 +106,46 @@ export default function withTheme<Props: {}>(
81106
: this._root;
82107
};
83108

84-
// setNativeProps is used by Animated to set props on the native component
85-
/* $FlowFixMe */
86-
if (Comp.prototype.setNativeProps) {
87-
// $FlowFixMe
88-
ThemedComponent.prototype.setNativeProps = function setNativeProps(
89-
...args
90-
) {
91-
const root = this.getWrappedInstance();
92-
return root.setNativeProps(...args);
93-
};
94-
}
109+
// Copy non-private methods and properties from underlying component
110+
// This will take expose public methods and properties such as `focus`, `setNativeProps` etc.
111+
// $FlowFixMe
112+
Object.getOwnPropertyNames(Comp.prototype)
113+
.filter(
114+
prop =>
115+
!(
116+
REACT_METHODS.includes(prop) || // React specific methods and properties
117+
prop in React.Component.prototype || // Properties from React's prototype such as `setState`
118+
prop in ThemedComponent.prototype || // Properties from enhanced component's prototype
119+
// Private methods
120+
prop.startsWith('_')
121+
)
122+
)
123+
.forEach(prop => {
124+
// $FlowFixMe
125+
if (typeof Comp.prototype[prop] === 'function') {
126+
/* eslint-disable func-names */
127+
// $FlowFixMe
128+
ThemedComponent.prototype[prop] = function(...args) {
129+
// Make sure the function is called with correct context
130+
// $FlowFixMe
131+
Comp.prototype[prop].apply(this.getWrappedInstance(), args);
132+
};
133+
// Set the function name for better debugging
134+
// $FlowFixMe
135+
ThemedComponent.prototype[prop].name = prop;
136+
} else {
137+
// Copy properties as getters and setters
138+
// This make sure dynamic properties always stay up-to-date
139+
Object.defineProperty(ThemedComponent.prototype, prop, {
140+
get() {
141+
return this.getWrappedInstance()[prop];
142+
},
143+
set(value) {
144+
this.getWrappedInstance()[prop] = value;
145+
},
146+
});
147+
}
148+
});
95149
}
96150

97151
hoistNonReactStatics(ThemedComponent, Comp);

0 commit comments

Comments
 (0)