From f8366d010d549f06d4c8c4ca16eb9cfef3400fea Mon Sep 17 00:00:00 2001 From: CHaBou Date: Sat, 16 Sep 2017 21:17:22 +0200 Subject: [PATCH] Upgrade React to v16 * Introduce 2 base components: Component and PureComponent. Before, we have only PureComponent but it was impossible to add a shoulComponentUpdate method (used for Terms). --- lib/base-components.js | 69 +++++++++++++++++++++++++++++++++ lib/component.js | 64 ------------------------------ lib/components/header.js | 4 +- lib/components/notification.js | 4 +- lib/components/notifications.js | 4 +- lib/components/split-pane.js | 4 +- lib/components/tab.js | 4 +- lib/components/tabs.js | 4 +- lib/components/term-group.js | 4 +- lib/components/term.js | 4 +- lib/components/terms.js | 2 +- lib/containers/hyper.js | 4 +- lib/utils/plugins.js | 6 +-- package.json | 4 +- yarn.lock | 29 +++++--------- 15 files changed, 103 insertions(+), 107 deletions(-) create mode 100644 lib/base-components.js delete mode 100644 lib/component.js diff --git a/lib/base-components.js b/lib/base-components.js new file mode 100644 index 00000000..7b7af94d --- /dev/null +++ b/lib/base-components.js @@ -0,0 +1,69 @@ +import React from 'react'; +import {StyleSheet, css} from 'aphrodite-simple'; + +const decorateBaseComponent = Component => { + return class BaseComponent extends Component { + constructor() { + super(); + this.styles_ = this.createStyleSheet(); + this.cssHelper = this.cssHelper.bind(this); + } + + createStyleSheet() { + if (!this.styles) { + return {}; + } + + const styles = this.styles(); + + if (typeof styles !== 'object') { + throw new TypeError('Component `styles` returns a non-object'); + } + + return StyleSheet.create(this.styles()); + } + + // wrap aphrodite's css helper for two reasons: + // - we can give the element an unaltered global classname + // that can be used to introduce global css side effects + // for example, through the configuration, web inspector + // or user agent extensions + // - the user doesn't need to keep track of both `css` + // and `style`, and we make that whole ordeal easier + cssHelper(...args) { + const classes = args + .map(c => { + if (c) { + // we compute the global name from the given + // css class and we prepend the component name + // + // it's important classes never get mangled by + // uglifiers so that we can avoid collisions + const component = this.constructor.name.toString().toLowerCase(); + const globalName = `${component}_${c}`; + return [globalName, css(this.styles_[c])]; + } + return null; + }) + // skip nulls + .filter(v => Boolean(v)) + // flatten + .reduce((a, b) => a.concat(b)); + return classes.length ? classes.join(' ') : null; + } + + render() { + // convert static objects from `babel-plugin-transform-jsx` + // to `React.Element`. + if (!this.template) { + throw new TypeError("Component doesn't define `template`"); + } + + // invoke the template creator passing our css helper + return this.template(this.cssHelper); + } + }; +}; + +export const PureComponent = decorateBaseComponent(React.PureComponent); +export const Component = decorateBaseComponent(React.Component); diff --git a/lib/component.js b/lib/component.js deleted file mode 100644 index e5a98990..00000000 --- a/lib/component.js +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import {StyleSheet, css} from 'aphrodite-simple'; - -export default class Component extends React.PureComponent { - constructor() { - super(); - this.styles_ = this.createStyleSheet(); - this.cssHelper = this.cssHelper.bind(this); - } - - createStyleSheet() { - if (!this.styles) { - return {}; - } - - const styles = this.styles(); - - if (typeof styles !== 'object') { - throw new TypeError('Component `styles` returns a non-object'); - } - - return StyleSheet.create(this.styles()); - } - - // wrap aphrodite's css helper for two reasons: - // - we can give the element an unaltered global classname - // that can be used to introduce global css side effects - // for example, through the configuration, web inspector - // or user agent extensions - // - the user doesn't need to keep track of both `css` - // and `style`, and we make that whole ordeal easier - cssHelper(...args) { - const classes = args - .map(c => { - if (c) { - // we compute the global name from the given - // css class and we prepend the component name - // - // it's important classes never get mangled by - // uglifiers so that we can avoid collisions - const component = this.constructor.name.toString().toLowerCase(); - const globalName = `${component}_${c}`; - return [globalName, css(this.styles_[c])]; - } - return null; - }) - // skip nulls - .filter(v => Boolean(v)) - // flatten - .reduce((a, b) => a.concat(b)); - return classes.length ? classes.join(' ') : null; - } - - render() { - // convert static objects from `babel-plugin-transform-jsx` - // to `React.Element`. - if (!this.template) { - throw new TypeError("Component doesn't define `template`"); - } - - // invoke the template creator passing our css helper - return this.template(this.cssHelper); - } -} diff --git a/lib/components/header.js b/lib/components/header.js index c7d4b347..b1a618b1 100644 --- a/lib/components/header.js +++ b/lib/components/header.js @@ -1,13 +1,13 @@ import React from 'react'; -import Component from '../component'; +import {PureComponent} from '../base-components'; import {decorate, getTabsProps} from '../utils/plugins'; import Tabs_ from './tabs'; const Tabs = decorate(Tabs_, 'Tabs'); -export default class Header extends Component { +export default class Header extends PureComponent { constructor() { super(); this.onChangeIntent = this.onChangeIntent.bind(this); diff --git a/lib/components/notification.js b/lib/components/notification.js index d28af573..bb2c421e 100644 --- a/lib/components/notification.js +++ b/lib/components/notification.js @@ -1,7 +1,7 @@ import React from 'react'; -import Component from '../component'; +import {PureComponent} from '../base-components'; -export default class Notification extends Component { +export default class Notification extends PureComponent { constructor() { super(); this.state = { diff --git a/lib/components/notifications.js b/lib/components/notifications.js index d6e0bf70..a70dc77a 100644 --- a/lib/components/notifications.js +++ b/lib/components/notifications.js @@ -1,13 +1,13 @@ import React from 'react'; -import Component from '../component'; +import {PureComponent} from '../base-components'; import {decorate} from '../utils/plugins'; import Notification_ from './notification'; const Notification = decorate(Notification_); -export default class Notifications extends Component { +export default class Notifications extends PureComponent { template(css) { return (
diff --git a/lib/components/split-pane.js b/lib/components/split-pane.js index 031fc681..7b0016c1 100644 --- a/lib/components/split-pane.js +++ b/lib/components/split-pane.js @@ -1,8 +1,8 @@ /* eslint-disable quote-props */ import React from 'react'; -import Component from '../component'; +import {PureComponent} from '../base-components'; -export default class SplitPane extends Component { +export default class SplitPane extends PureComponent { constructor(props) { super(props); this.handleDragStart = this.handleDragStart.bind(this); diff --git a/lib/components/tab.js b/lib/components/tab.js index ee204331..1f2a871d 100644 --- a/lib/components/tab.js +++ b/lib/components/tab.js @@ -1,7 +1,7 @@ import React from 'react'; -import Component from '../component'; +import {PureComponent} from '../base-components'; -export default class Tab extends Component { +export default class Tab extends PureComponent { constructor() { super(); diff --git a/lib/components/tabs.js b/lib/components/tabs.js index 91e2ef59..38485ca1 100644 --- a/lib/components/tabs.js +++ b/lib/components/tabs.js @@ -1,6 +1,6 @@ import React from 'react'; -import Component from '../component'; +import {PureComponent} from '../base-components'; import {decorate, getTabProps} from '../utils/plugins'; import Tab_ from './tab'; @@ -8,7 +8,7 @@ import Tab_ from './tab'; const Tab = decorate(Tab_, 'Tab'); const isMac = /Mac/.test(navigator.userAgent); -export default class Tabs extends Component { +export default class Tabs extends PureComponent { template(css) { const {tabs = [], borderColor, onChange, onClose} = this.props; diff --git a/lib/components/term-group.js b/lib/components/term-group.js index d9a67318..69f7d1d6 100644 --- a/lib/components/term-group.js +++ b/lib/components/term-group.js @@ -1,6 +1,6 @@ import React from 'react'; import {connect} from 'react-redux'; -import Component from '../component'; +import {PureComponent} from '../base-components'; import {decorate, getTermProps, getTermGroupProps} from '../utils/plugins'; import {resizeTermGroup} from '../actions/term-groups'; import Term_ from './term'; @@ -9,7 +9,7 @@ import SplitPane_ from './split-pane'; const Term = decorate(Term_, 'Term'); const SplitPane = decorate(SplitPane_, 'SplitPane'); -class TermGroup_ extends Component { +class TermGroup_ extends PureComponent { constructor(props, context) { super(props, context); this.bound = new WeakMap(); diff --git a/lib/components/term.js b/lib/components/term.js index 3fabf9d9..f8c6900c 100644 --- a/lib/components/term.js +++ b/lib/components/term.js @@ -1,7 +1,7 @@ /* global Blob,URL,requestAnimationFrame */ import React from 'react'; import Terminal from 'xterm'; -import Component from '../component'; +import {PureComponent} from '../base-components'; import terms from '../terms'; import returnKey from '../utils/keymaps'; import CommandRegistry from '../command-registry'; @@ -13,7 +13,7 @@ const CURSOR_STYLES = { BLOCK: 'block' }; -export default class Term extends Component { +export default class Term extends PureComponent { constructor(props) { super(props); props.ref_(props.uid, this); diff --git a/lib/components/terms.js b/lib/components/terms.js index ffd55017..97cc562a 100644 --- a/lib/components/terms.js +++ b/lib/components/terms.js @@ -1,5 +1,5 @@ import React from 'react'; -import Component from '../component'; +import {Component} from '../base-components'; import {decorate, getTermGroupProps} from '../utils/plugins'; import CommandRegistry from '../command-registry'; import TermGroup_ from './term-group'; diff --git a/lib/containers/hyper.js b/lib/containers/hyper.js index 4a9ca610..46dc2539 100644 --- a/lib/containers/hyper.js +++ b/lib/containers/hyper.js @@ -2,7 +2,7 @@ import React from 'react'; -import Component from '../component'; +import {PureComponent} from '../base-components'; import {connect} from '../utils/plugins'; import * as uiActions from '../actions/ui'; @@ -12,7 +12,7 @@ import NotificationsContainer from './notifications'; const isMac = /Mac/.test(navigator.userAgent); -class Hyper extends Component { +class Hyper extends PureComponent { constructor(props) { super(props); this.handleFocusActive = this.handleFocusActive.bind(this); diff --git a/lib/utils/plugins.js b/lib/utils/plugins.js index a86555ad..b7bcf36a 100644 --- a/lib/utils/plugins.js +++ b/lib/utils/plugins.js @@ -6,7 +6,7 @@ import {connect as reduxConnect} from 'react-redux'; // https://github.com/zeit/hyper/issues/619 import React from 'react'; import ReactDOM from 'react-dom'; -import Component from '../component'; +import {PureComponent} from '../base-components'; import Notification from '../components/notification'; import notify from './notify'; @@ -20,7 +20,7 @@ Module._load = function _load(path) { case 'react-dom': return ReactDOM; case 'hyper/component': - return Component; + return PureComponent; case 'hyper/notify': return notify; case 'hyper/Notification': @@ -457,7 +457,7 @@ function getDecorated(parent, name) { let class__; try { - class__ = fn(class_, {React, Component, Notification, notify}); + class__ = fn(class_, {React, PureComponent, Notification, notify}); class__.displayName = `${fn._pluginName}(${name})`; } catch (err) { //eslint-disable-next-line no-console diff --git a/package.json b/package.json index 2ce6fc4c..7f2c4b40 100644 --- a/package.json +++ b/package.json @@ -150,9 +150,9 @@ "mousetrap": "1.6.1", "ms": "2.0.0", "php-escape-shell": "1.0.0", - "react": "15.6.1", + "react": "16.0.0-rc.3", "react-deep-force-update": "2.0.1", - "react-dom": "15.6.1", + "react-dom": "16.0.0-rc.3", "react-redux": "5.0.6", "redux": "3.7.2", "redux-thunk": "2.2.0", diff --git a/yarn.lock b/yarn.lock index 5ccb9288..108a4d45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1725,14 +1725,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -create-react-class@^15.6.0: - version "15.6.0" - resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.0.tgz#ab448497c26566e1e29413e883207d57cfe7bed4" - dependencies: - fbjs "^0.8.9" - loose-envify "^1.3.1" - object-assign "^4.1.1" - cross-env@5.0.5: version "5.0.5" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.0.5.tgz#4383d364d9660873dd185b398af3bfef5efffef3" @@ -4178,7 +4170,7 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -4814,7 +4806,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.5.10: +prop-types@^15.5.10, prop-types@^15.5.6: version "15.5.10" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" dependencies: @@ -4896,14 +4888,14 @@ react-deep-force-update@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/react-deep-force-update/-/react-deep-force-update-2.0.1.tgz#4f7f6c12c3e7de42f345992a3c518236fa1ecad3" -react-dom@15.6.1: - version "15.6.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.1.tgz#2cb0ed4191038e53c209eb3a79a23e2a4cf99470" +react-dom@16.0.0-rc.3: + version "16.0.0-rc.3" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.0.0-rc.3.tgz#bd4e4d2abd464df5149a062b45fe796bbb804c2c" dependencies: fbjs "^0.8.9" loose-envify "^1.1.0" object-assign "^4.1.0" - prop-types "^15.5.10" + prop-types "^15.5.6" react-redux@5.0.6: version "5.0.6" @@ -4916,15 +4908,14 @@ react-redux@5.0.6: loose-envify "^1.1.0" prop-types "^15.5.10" -react@15.6.1: - version "15.6.1" - resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df" +react@16.0.0-rc.3: + version "16.0.0-rc.3" + resolved "https://registry.yarnpkg.com/react/-/react-16.0.0-rc.3.tgz#4a9df996326ba7185903d9fbed3149765e237e26" dependencies: - create-react-class "^15.6.0" fbjs "^0.8.9" loose-envify "^1.1.0" object-assign "^4.1.0" - prop-types "^15.5.10" + prop-types "^15.5.6" read-config-file@1.1.0: version "1.1.0"