Skip to content

Commit 246b3cd

Browse files
committed
Fix #25
1 parent 29ab2a0 commit 246b3cd

File tree

2 files changed

+96
-16
lines changed

2 files changed

+96
-16
lines changed

src/components/themr.js

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ export default (componentName, localTheme, options = {}) => (ThemedComponent) =>
4848
composeTheme: optionComposeTheme
4949
}
5050

51+
constructor(...args) {
52+
super(...args)
53+
this.theme_ = this.calcTheme(this.props)
54+
}
55+
5156
getWrappedInstance() {
5257
invariant(optionWithRef,
5358
'To access the wrapped instance, you need to specify ' +
@@ -57,8 +62,8 @@ export default (componentName, localTheme, options = {}) => (ThemedComponent) =>
5762
return this.refs.wrappedInstance
5863
}
5964

60-
getNamespacedTheme() {
61-
const { themeNamespace, theme } = this.props
65+
getNamespacedTheme(props) {
66+
const { themeNamespace, theme } = props
6267
if (!themeNamespace) return theme
6368
if (themeNamespace && !theme) throw new Error('Invalid themeNamespace use in react-css-themr. ' +
6469
'themeNamespace prop should be used only with theme prop.')
@@ -68,8 +73,8 @@ export default (componentName, localTheme, options = {}) => (ThemedComponent) =>
6873
.reduce((result, key) => ({ ...result, [removeNamespace(key, themeNamespace)]: theme[key] }), {})
6974
}
7075

71-
getThemeNotComposed() {
72-
if (this.props.theme) return this.getNamespacedTheme()
76+
getThemeNotComposed(props) {
77+
if (props.theme) return this.getNamespacedTheme(props)
7378
if (config.localTheme) return config.localTheme
7479
return this.getContextTheme()
7580
}
@@ -80,30 +85,50 @@ export default (componentName, localTheme, options = {}) => (ThemedComponent) =>
8085
: {}
8186
}
8287

83-
getTheme() {
84-
return this.props.composeTheme === COMPOSE_SOFTLY
85-
? { ...this.getContextTheme(), ...config.localTheme, ...this.getNamespacedTheme() }
86-
: themeable(themeable(this.getContextTheme(), config.localTheme), this.getNamespacedTheme())
88+
getTheme(props) {
89+
return props.composeTheme === COMPOSE_SOFTLY
90+
? {
91+
...this.getContextTheme(),
92+
...config.localTheme,
93+
...this.getNamespacedTheme(props)
94+
}
95+
: themeable(
96+
themeable(this.getContextTheme(), config.localTheme),
97+
this.getNamespacedTheme(props)
98+
)
99+
}
100+
101+
calcTheme(props) {
102+
const { composeTheme } = props
103+
return composeTheme
104+
? this.getTheme(props)
105+
: this.getThemeNotComposed(props)
106+
}
107+
108+
componentWillReceiveProps(nextProps) {
109+
if (
110+
nextProps.composeTheme !== this.props.composeTheme ||
111+
nextProps.theme !== this.props.theme ||
112+
nextProps.themeNamespace !== this.props.themeNamespace
113+
) {
114+
this.theme_ = this.calcTheme(nextProps)
115+
}
87116
}
88117

89118
render() {
90-
const { composeTheme, ...rest } = this.props
119+
const { ...rest } = this.props
91120
let renderedElement
92121

93122
if (optionWithRef) {
94123
renderedElement = React.createElement(ThemedComponent, {
95124
...rest,
96125
ref: 'wrappedInstance',
97-
theme: composeTheme
98-
? this.getTheme()
99-
: this.getThemeNotComposed()
126+
theme: this.theme_
100127
})
101128
} else {
102129
renderedElement = React.createElement(ThemedComponent, {
103130
...rest,
104-
theme: composeTheme
105-
? this.getTheme()
106-
: this.getThemeNotComposed()
131+
theme: this.theme_
107132
})
108133
}
109134

test/components/themr.spec.js

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,61 @@ describe('Themr decorator function', () => {
438438
)
439439

440440
expect(spy.calledOnce).toBe(true)
441-
442441
})
442+
443+
it(
444+
'should update theme prop on rerender if theme or themeNamespace or composeTheme changed',
445+
() => {
446+
const spy = sinon.stub().returns(<div />)
447+
const div = document.createElement('div')
448+
449+
@themr('Container')
450+
class Container extends Component {
451+
shouldComponentUpdate(nextProps) {
452+
return !shallowEqual(nextProps, this.props)
453+
}
454+
455+
render() {
456+
return spy()
457+
}
458+
}
459+
const themeA = {}
460+
const themeB = {}
461+
const themeNamespace = 'nsA'
462+
463+
render(
464+
<Container theme={themeA} />,
465+
div
466+
)
467+
468+
render(
469+
<Container theme={themeB} />,
470+
div
471+
)
472+
473+
expect(spy.calledTwice).toBe(true)
474+
475+
render(
476+
<Container theme={themeB} themeNamespace={themeNamespace} />,
477+
div
478+
)
479+
480+
expect(spy.calledThrice).toBe(true)
481+
482+
483+
render(
484+
<Container theme={themeB} themeNamespace={themeNamespace} composeTheme={'deeply'} />,
485+
div
486+
)
487+
488+
expect(spy.calledThrice).toBe(true)
489+
490+
render(
491+
<Container theme={themeB} themeNamespace={themeNamespace} composeTheme={'softly'} />,
492+
div
493+
)
494+
495+
expect(spy.callCount === 4).toBe(true)
496+
}
497+
)
443498
})

0 commit comments

Comments
 (0)