From ef04138f3ce8297b98aafb6952cd64ce83666c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Phil=20Pl=C3=BCckthun?= Date: Thu, 20 Apr 2017 00:22:12 +0100 Subject: [PATCH 1/4] Hoist non-static properties on withTheme HOC Fix #596 See for reference: - https://github.com/reactjs/react-redux/issues/53 - https://github.com/reactjs/react-redux/issues/276 --- package.json | 1 + src/hoc/withTheme.js | 57 ++++++++++++++++++++++++++---------------- src/test/theme.test.js | 11 ++++++++ yarn.lock | 4 +++ 4 files changed, 52 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index d4e171b17..57954fc30 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "buffer": "^5.0.3", "css-to-react-native": "^2.0.3", "fbjs": "^0.8.9", + "hoist-non-react-statics": "^1.2.0", "inline-style-prefixer": "^2.0.5", "is-function": "^1.0.1", "is-plain-object": "^2.0.1", diff --git a/src/hoc/withTheme.js b/src/hoc/withTheme.js index 2e64c7689..f811092e3 100644 --- a/src/hoc/withTheme.js +++ b/src/hoc/withTheme.js @@ -3,34 +3,49 @@ import React from 'react' import PropTypes from 'prop-types' +import hoistStatics from 'hoist-non-react-statics' import { CHANNEL } from '../models/ThemeProvider' -export default (Component: ReactClass) => class extends React.Component { - static contextTypes = { - [CHANNEL]: PropTypes.func, - }; +const wrapWithTheme = (Component: ReactClass) => { + const componentName = ( + Component.displayName || + Component.name || + 'Component' + ) - state: { theme?: ?Object } = {}; - unsubscribe: () => void; + class WithTheme extends React.Component { + static displayName = `WithTheme(${componentName})` - componentWillMount() { - if (!this.context[CHANNEL]) { - throw new Error('[withTheme] Please use ThemeProvider to be able to use withTheme') - } + static contextTypes = { + [CHANNEL]: PropTypes.func, + }; - const subscribe = this.context[CHANNEL] - this.unsubscribe = subscribe(theme => { - this.setState({ theme }) - }) - } + state: { theme?: ?Object } = {}; + unsubscribe: () => void; - componentWillUnmount() { - if (typeof this.unsubscribe === 'function') this.unsubscribe() - } + componentWillMount() { + if (!this.context[CHANNEL]) { + throw new Error('[withTheme] Please use ThemeProvider to be able to use withTheme') + } + + const subscribe = this.context[CHANNEL] + this.unsubscribe = subscribe(theme => { + this.setState({ theme }) + }) + } - render() { - const { theme } = this.state + componentWillUnmount() { + if (typeof this.unsubscribe === 'function') this.unsubscribe() + } + + render() { + const { theme } = this.state - return + return + } } + + return hoistStatics(WithTheme, Component) } + +export default wrapWithTheme diff --git a/src/test/theme.test.js b/src/test/theme.test.js index ce4cdd4ea..1bd5a0c46 100644 --- a/src/test/theme.test.js +++ b/src/test/theme.test.js @@ -372,4 +372,15 @@ describe('theming', () => { expectCSSMatches(`.sc-a { } .b { color: green; } `) }) + + // https://github.com/styled-components/styled-components/issues/596 + it('should hoist static properties when using withTheme', () => { + class MyComponent extends React.Component { + static myStaticProperty: boolean = true + } + + const MyComponentWithTheme = withTheme(MyComponent) + + expect(MyComponentWithTheme.myStaticProperty).toBe(true) + }) }) diff --git a/yarn.lock b/yarn.lock index 49f0a755b..b193bb138 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2665,6 +2665,10 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" +hoist-non-react-statics@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" + home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" From 6fba590e14b15ababf6130ebf319afbead4facff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Phil=20Pl=C3=BCckthun?= Date: Thu, 20 Apr 2017 00:28:00 +0100 Subject: [PATCH 2/4] Add entry to CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb85a9fa0..6e586fa9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All notable changes to this project will be documented in this file. If a contri - Prevent leakage of the `innerRef` prop to wrapped child; under the hood it is converted into a normal React `ref`. (see [#592](https://github.com/styled-components/styled-components/issues/592)) - Pass `innerRef` through to wrapped Styled Components, so that it refers to the actual DOM node. (see [#629](https://github.com/styled-components/styled-components/issues/629)) - Added a dedicated Server-Side-Rendering API, with optimised rehydration on the client. +- Add hoisting static (non-React) properties for withTheme HOC. (See [#712](https://github.com/styled-components/styled-components/pull/712)) ## [Unreleased] From f95df27f5ddf7d4d1f0472986aa07139bef2aece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Phil=20Pl=C3=BCckthun?= Date: Thu, 20 Apr 2017 01:08:32 +0100 Subject: [PATCH 3/4] Fix withTheme ReactClass type --- src/hoc/withTheme.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hoc/withTheme.js b/src/hoc/withTheme.js index f811092e3..7ee12ce54 100644 --- a/src/hoc/withTheme.js +++ b/src/hoc/withTheme.js @@ -6,7 +6,7 @@ import PropTypes from 'prop-types' import hoistStatics from 'hoist-non-react-statics' import { CHANNEL } from '../models/ThemeProvider' -const wrapWithTheme = (Component: ReactClass) => { +const wrapWithTheme = (Component: ReactClass) => { const componentName = ( Component.displayName || Component.name || From 73e1687679c9621620ea8e3040bb647f856d3675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Phil=20Pl=C3=BCckthun?= Date: Thu, 20 Apr 2017 01:26:16 +0100 Subject: [PATCH 4/4] Add attribution to @brunolemos to Changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e586fa9f..820a1c031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ All notable changes to this project will be documented in this file. If a contri - Prevent leakage of the `innerRef` prop to wrapped child; under the hood it is converted into a normal React `ref`. (see [#592](https://github.com/styled-components/styled-components/issues/592)) - Pass `innerRef` through to wrapped Styled Components, so that it refers to the actual DOM node. (see [#629](https://github.com/styled-components/styled-components/issues/629)) - Added a dedicated Server-Side-Rendering API, with optimised rehydration on the client. -- Add hoisting static (non-React) properties for withTheme HOC. (See [#712](https://github.com/styled-components/styled-components/pull/712)) +- Add hoisting static (non-React) properties for withTheme HOC, thanks to [@brunolemos](https://github.com/brunolemos). (See [#712](https://github.com/styled-components/styled-components/pull/712)) ## [Unreleased]