Skip to content

Commit f583157

Browse files
committed
refactor: rework theme provider and HOC
1 parent 0a20307 commit f583157

File tree

3 files changed

+69
-19
lines changed

3 files changed

+69
-19
lines changed

.flowconfig

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
.*/docs/node_modules/fbjs/.*
3636
.*/docs/node_modules/react/.*
3737

38+
; Ignore some modules we don't need to parse
39+
.*/node_modules/prettier/.*
40+
.*/node_modules/eslint.*
41+
3842
[include]
3943

4044
[libs]
@@ -66,4 +70,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
6670
unsafe.enable_getters_and_setters=true
6771

6872
[version]
69-
^0.40.0
73+
^0.40.0

src/core/ThemeProvider.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type Props = {
1414
theme?: Theme,
1515
};
1616

17-
export const theme = 'react-native-paper$theme';
17+
export const channel = 'react-native-paper$theme';
1818

1919
export default class ThemeProvider
2020
extends PureComponent<DefaultProps, Props, void> {
@@ -28,15 +28,41 @@ export default class ThemeProvider
2828
};
2929

3030
static childContextTypes = {
31-
[theme]: PropTypes.object,
31+
[channel]: PropTypes.object,
3232
};
3333

3434
getChildContext() {
3535
return {
36-
[theme]: this.props.theme,
36+
[channel]: {
37+
subscribe: this._subscribe,
38+
get: this._get,
39+
},
3740
};
3841
}
3942

43+
componentWillReceiveProps(nextProps: *) {
44+
if (this.props.theme !== nextProps.theme) {
45+
this._subscriptions.forEach(cb => cb(nextProps.theme));
46+
}
47+
}
48+
49+
_subscriptions = [];
50+
51+
_subscribe = (callback: Theme => void) => {
52+
this._subscriptions.push(callback);
53+
54+
const remove = () => {
55+
const index = this._subscriptions.indexOf(callback);
56+
if (index > -1) {
57+
this._subscriptions.splice(index, 1);
58+
}
59+
};
60+
61+
return { remove };
62+
};
63+
64+
_get = () => this.props.theme;
65+
4066
render() {
4167
return Children.only(this.props.children);
4268
}

src/core/withTheme.js

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22

33
import React, { PureComponent } from 'react';
44
import PropTypes from 'prop-types';
5-
import { theme } from './ThemeProvider';
5+
import { channel } from './ThemeProvider';
66
import type { Theme } from '../types/Theme';
77

88
type State = {
99
theme: Theme,
1010
};
1111

12-
export default function withTheme<T: *>(Comp: ReactClass<T>): ReactClass<T> {
12+
export default function withThemeName<T: *>(
13+
Comp: ReactClass<T>
14+
): ReactClass<T> {
1315
class ThemedComponent extends PureComponent<void, *, State> {
1416
static displayName = `withTheme(${Comp.displayName || Comp.name})`;
1517

@@ -18,38 +20,55 @@ export default function withTheme<T: *>(Comp: ReactClass<T>): ReactClass<T> {
1820
};
1921

2022
static contextTypes = {
21-
[theme]: PropTypes.object,
23+
[channel]: PropTypes.object,
2224
};
2325

2426
constructor(props, context) {
2527
super(props, context);
28+
29+
const theme = this.context[channel] && this.context[channel].get();
30+
31+
if (typeof theme !== 'object' && typeof this.props.theme !== 'object') {
32+
throw new Error(
33+
`Couldn't find theme in the context or props. ` +
34+
`You need to wrap your component in '<ThemeProvider />' or pass a 'theme' prop`
35+
);
36+
}
37+
2638
this.state = {
27-
theme: this._merge(context[theme], props.theme),
39+
theme: this._merge(theme, this.props.theme),
2840
};
2941
}
3042

3143
state: State;
3244

3345
componentDidMount() {
34-
if (typeof this.state.theme !== 'object' || this.state.theme === null) {
35-
throw new Error(
36-
"Couldn't find theme in the context or props. " +
37-
"You need to wrap your component in '<ThemeProvider />' or pass a 'theme' prop"
46+
this._subscription =
47+
this.context[channel] &&
48+
this.context[channel].subscribe(theme =>
49+
this.setState({ theme: this._merge(theme, this.props.theme) })
3850
);
39-
}
4051
}
4152

42-
componentWillReceiveProps(nextProps, nextContext: any) {
43-
if (
44-
nextProps.theme !== this.props.theme ||
45-
nextContext[theme] !== this.context[theme]
46-
) {
53+
componentWillReceiveProps(nextProps: *) {
54+
if (this.props.theme !== nextProps.theme) {
4755
this.setState({
48-
theme: this._merge(nextContext[theme], nextProps.theme),
56+
theme: this._merge(
57+
this.context[channel] && this.context[channel].get(),
58+
nextProps.theme
59+
),
4960
});
5061
}
5162
}
5263

64+
componentWillUnmount() {
65+
this._subscription && this._subscription.remove();
66+
}
67+
68+
getWrappedInstance() {
69+
return this._root;
70+
}
71+
5372
setNativeProps(...args) {
5473
return this._root.setNativeProps(...args);
5574
}
@@ -62,6 +81,7 @@ export default function withTheme<T: *>(Comp: ReactClass<T>): ReactClass<T> {
6281
}
6382
};
6483

84+
_subscription: Object;
6585
_root: any;
6686

6787
render() {

0 commit comments

Comments
 (0)