refactor: Use ES6 classes instead of createClass

This commit is contained in:
Tom MacWright
2017-05-11 11:04:12 -04:00
committed by Tom MacWright
parent 5657bb232a
commit f5c5f86b24
9 changed files with 89 additions and 106 deletions

View File

@ -1,4 +1,5 @@
{ {
"plugins": ["transform-class-properties"],
"presets": [ "presets": [
"stage-0", "stage-0",
"es2015", "es2015",

View File

@ -34,9 +34,8 @@
"react/react-in-jsx-scope": 2, "react/react-in-jsx-scope": 2,
"react/no-unknown-property": [2], "react/no-unknown-property": [2],
"react/prop-types": 2, "react/prop-types": 2,
"react/require-extension": [2, { "extensions": [".js", ".jsx"] }],
"react/self-closing-comp": [2], "react/self-closing-comp": [2],
"react/wrap-multilines": [2], "react/jsx-wrap-multilines": [2],
"semi": [2, "always"], "semi": [2, "always"],
"keyword-spacing": [2, { "before": true, "after": true }], "keyword-spacing": [2, { "before": true, "after": true }],
"space-before-blocks": 2, "space-before-blocks": 2,

View File

@ -27,6 +27,7 @@
"dependencies": { "dependencies": {
"babel-cli": "^6.4.0", "babel-cli": "^6.4.0",
"babel-eslint": "^7.2.1", "babel-eslint": "^7.2.1",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-polyfill": "^6.3.14", "babel-polyfill": "^6.3.14",
"babel-preset-es2015": "^6.3.13", "babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13", "babel-preset-react": "^6.3.13",
@ -44,9 +45,9 @@
"isomorphic-fetch": "^2.2.0", "isomorphic-fetch": "^2.2.0",
"lodash.debounce": "^4.0.3", "lodash.debounce": "^4.0.3",
"minifyify": "^7.1.0", "minifyify": "^7.1.0",
"prop-types": "^15.5.9",
"react": "^15.0.2", "react": "^15.0.2",
"react-dom": "^15.0.2", "react-dom": "^15.0.2",
"react-pure-render": "^1.0.2",
"remark": "^7.0.0", "remark": "^7.0.0",
"remark-html": "^6.0.0", "remark-html": "^6.0.0",
"remark-slug": "^4.1.0", "remark-slug": "^4.1.0",

View File

@ -1,8 +1,8 @@
import React from 'react'; import React from 'react';
import Navigation from './navigation'; import Navigation from './navigation';
import PropTypes from 'prop-types';
import Content from './content'; import Content from './content';
import RoundedToggle from './rounded_toggle'; import RoundedToggle from './rounded_toggle';
import PureRenderMixin from 'react-pure-render/mixin';
import GithubSlugger from 'github-slugger'; import GithubSlugger from 'github-slugger';
import debounce from 'lodash.debounce'; import debounce from 'lodash.debounce';
import { brandNames, brandClasses } from '../custom'; import { brandNames, brandClasses } from '../custom';
@ -41,13 +41,13 @@ let debouncedReplaceState = debounce(hash => {
window.history.replaceState('', '', hash); window.history.replaceState('', '', hash);
}, 100); }, 100);
var App = React.createClass({ export default class App extends React.PureComponent {
mixins: [PureRenderMixin], static propTypes = {
propTypes: { content: PropTypes.string.isRequired,
content: React.PropTypes.string.isRequired, ast: PropTypes.object.isRequired
ast: React.PropTypes.object.isRequired }
}, constructor(props) {
getInitialState() { super(props);
var active = 'Introduction'; var active = 'Introduction';
if (process.browser) { if (process.browser) {
@ -70,7 +70,7 @@ var App = React.createClass({
active = headingForHash.children[0].value; active = headingForHash.children[0].value;
} }
} }
return { this.state = {
// media queryMatches // media queryMatches
mqls: mqls, mqls: mqls,
// object of currently matched queries, like { desktop: true } // object of currently matched queries, like { desktop: true }
@ -82,7 +82,7 @@ var App = React.createClass({
showNav: false showNav: false
}; };
} else { } else {
return { this.state = {
mqls: { }, mqls: { },
queryMatches: { queryMatches: {
desktop: true desktop: true
@ -92,17 +92,17 @@ var App = React.createClass({
showNav: false showNav: false
}; };
} }
}, }
toggleNav() { toggleNav() {
this.setState({ showNav: !this.state.showNav }); this.setState({ showNav: !this.state.showNav });
}, }
componentDidMount() { componentDidMount() {
this.mediaQueryChanged(); this.mediaQueryChanged();
this.onScroll = debounce(this.onScrollImmediate, 100); this.onScroll = debounce(this.onScrollImmediate, 100);
document.addEventListener('scroll', this.onScroll); document.addEventListener('scroll', this.onScroll);
this.onScrollImmediate(); this.onScrollImmediate();
}, }
onScrollImmediate() { onScrollImmediate = () => {
var sections = document.querySelectorAll('div.section'); var sections = document.querySelectorAll('div.section');
if (!sections.length) return; if (!sections.length) return;
for (var i = 0; i < sections.length; i++) { for (var i = 0; i < sections.length; i++) {
@ -114,7 +114,7 @@ var App = React.createClass({
return; return;
} }
} }
}, }
mediaQueryChanged() { mediaQueryChanged() {
this.setState({ this.setState({
queryMatches: this.state.mqls.reduce((memo, q) => { queryMatches: this.state.mqls.reduce((memo, q) => {
@ -122,19 +122,19 @@ var App = React.createClass({
return memo; return memo;
}, {}) }, {})
}); });
}, }
componentWillUnmount() { componentWillUnmount() {
this.state.mqls.forEach(q => q.removeListener(this.mediaQueryChanged)); this.state.mqls.forEach(q => q.removeListener(this.mediaQueryChanged));
document.body.removeEventListener('scroll', this.onScroll); document.body.removeEventListener('scroll', this.onScroll);
}, }
onChangeLanguage(language) { onChangeLanguage = (language) => {
this.setState({ language }, () => { this.setState({ language }, () => {
if (window.history) { if (window.history) {
window.history.pushState(null, null, window.history.pushState(null, null,
`?${qs.stringify({ language: language.title })}${window.location.hash}`); `?${qs.stringify({ language: language.title })}${window.location.hash}`);
} }
}); });
}, }
componentDidUpdate(_, prevState) { componentDidUpdate(_, prevState) {
if (prevState.activeSection !== this.state.activeSection) { if (prevState.activeSection !== this.state.activeSection) {
// when the section changes, replace the hash // when the section changes, replace the hash
@ -144,7 +144,7 @@ var App = React.createClass({
// when the language changes, use the hash to set scroll // when the language changes, use the hash to set scroll
window.location.hash = window.location.hash; window.location.hash = window.location.hash;
} }
}, }
navigationItemClicked(activeSection) { navigationItemClicked(activeSection) {
setTimeout(() => { setTimeout(() => {
this.setState({ activeSection }); this.setState({ activeSection });
@ -152,12 +152,12 @@ var App = React.createClass({
if (!this.state.queryMatches.desktop) { if (!this.state.queryMatches.desktop) {
this.toggleNav(); this.toggleNav();
} }
}, }
toggleColumnMode() { toggleColumnMode() {
this.setState({ this.setState({
columnMode: this.state.columnMode === 1 ? 2 : 1 columnMode: this.state.columnMode === 1 ? 2 : 1
}); });
}, }
render() { render() {
let ast = JSON.parse(JSON.stringify(this.props.ast)); let ast = JSON.parse(JSON.stringify(this.props.ast));
let { activeSection, queryMatches, showNav, columnMode } = this.state; let { activeSection, queryMatches, showNav, columnMode } = this.state;
@ -237,6 +237,4 @@ var App = React.createClass({
</div>); </div>);
} }
}); }
module.exports = App;

View File

@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import Section from './section'; import Section from './section';
import PureRenderMixin from 'react-pure-render/mixin';
import GithubSlugger from 'github-slugger'; import GithubSlugger from 'github-slugger';
import { transformURL } from '../custom'; import { transformURL } from '../custom';
let slugger = new GithubSlugger(); let slugger = new GithubSlugger();
let slug = title => { slugger.reset(); return slugger.slug(title); }; let slug = title => { slugger.reset(); return slugger.slug(title); };
var roundedToggleOptionType = React.PropTypes.shape({ var roundedToggleOptionType = PropTypes.shape({
title: React.PropTypes.string, title: PropTypes.string,
value: React.PropTypes.string value: PropTypes.string
}); });
function chunkifyAST(ast, language) { function chunkifyAST(ast, language) {
@ -71,24 +71,21 @@ function chunkifyAST(ast, language) {
}); });
} }
var Content = React.createClass({ export default class Content extends React.PureComponent {
mixins: [PureRenderMixin], static propTypes = {
propTypes: { ast: PropTypes.object.isRequired,
ast: React.PropTypes.object.isRequired,
language: roundedToggleOptionType, language: roundedToggleOptionType,
leftClassname: React.PropTypes.string.isRequired, leftClassname: PropTypes.string.isRequired,
rightClassname: React.PropTypes.string.isRequired rightClassname: PropTypes.string.isRequired
}, }
render() { render() {
let { ast, language, leftClassname, rightClassname } = this.props; let { ast, language, leftClassname, rightClassname } = this.props;
return (<div className='clearfix'> return (<div className='clearfix'>
{chunkifyAST(ast, language.value).map((chunk, i) => <Section {chunkifyAST(ast, language.value).map((chunk, i) => (<Section
leftClassname={leftClassname} leftClassname={leftClassname}
rightClassname={rightClassname} rightClassname={rightClassname}
chunk={chunk} chunk={chunk}
key={i} />)} key={i} />))}
</div>); </div>);
} }
}); }
module.exports = Content;

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import PureRenderMixin from 'react-pure-render/mixin'; import PropTypes from 'prop-types';
import NavigationItem from './navigation_item'; import NavigationItem from './navigation_item';
import { footerContent } from '../custom'; import { footerContent } from '../custom';
@ -23,13 +23,12 @@ function getAllInSection(headings, idx) {
return activeHeadings; return activeHeadings;
} }
var Navigation = React.createClass({ export default class Navigation extends React.PureComponent {
mixins: [PureRenderMixin], static propTypes = {
propTypes: { ast: PropTypes.object.isRequired,
ast: React.PropTypes.object.isRequired, activeSection: PropTypes.string,
activeSection: React.PropTypes.string, navigationItemClicked: PropTypes.func.isRequired
navigationItemClicked: React.PropTypes.func.isRequired }
},
render() { render() {
var activeHeadings = []; var activeHeadings = [];
let headings = this.props.ast.children let headings = this.props.ast.children
@ -91,6 +90,4 @@ var Navigation = React.createClass({
{footerContent} {footerContent}
</div>); </div>);
} }
}); }
module.exports = Navigation;

View File

@ -1,17 +1,16 @@
import React from 'react'; import React from 'react';
import PureRenderMixin from 'react-pure-render/mixin'; import PropTypes from 'prop-types';
var NavigationItem = React.createClass({ export default class NavigationItem extends React.PureComponent {
mixins: [PureRenderMixin], static propTypes = {
propTypes: { sectionName: PropTypes.string.isRequired,
sectionName: React.PropTypes.string.isRequired, active: PropTypes.bool.isRequired,
active: React.PropTypes.bool.isRequired, onClick: PropTypes.func.isRequired,
onClick: React.PropTypes.func.isRequired, href: PropTypes.string.isRequired
href: React.PropTypes.string.isRequired }
}, onClick = () => {
onClick() {
this.props.onClick(this.props.sectionName); this.props.onClick(this.props.sectionName);
}, }
render() { render() {
var {sectionName, href, active} = this.props; var {sectionName, href, active} = this.props;
return (<a return (<a
@ -21,6 +20,4 @@ var NavigationItem = React.createClass({
{sectionName} {sectionName}
</a>); </a>);
} }
}); }
module.exports = NavigationItem;

View File

@ -1,50 +1,46 @@
import React from 'react'; import React from 'react';
import PureRenderMixin from 'react-pure-render/mixin'; import PropTypes from 'prop-types';
var roundedToggleOptionType = React.PropTypes.shape({ var roundedToggleOptionType = PropTypes.shape({
title: React.PropTypes.string, title: PropTypes.string,
value: React.PropTypes.string value: PropTypes.string
}); });
var RoundedToggle = React.createClass({ export default class RoundedToggle extends React.PureComponent {
mixins: [PureRenderMixin], static propTypes = {
propTypes: { options: PropTypes.arrayOf(roundedToggleOptionType).isRequired,
options: React.PropTypes.arrayOf(roundedToggleOptionType).isRequired,
active: roundedToggleOptionType, active: roundedToggleOptionType,
short: React.PropTypes.bool, short: PropTypes.bool,
onChange: React.PropTypes.func.isRequired onChange: PropTypes.func.isRequired
}, }
render() { render() {
let { options, active } = this.props; let { options, active } = this.props;
return (<div className='rounded-toggle inline short'> return (<div className='rounded-toggle inline short'>
{options.map(option => {options.map(option =>
<RoundedToggleOption (<RoundedToggleOption
key={option.value} key={option.value}
option={option} option={option}
short={this.props.short} short={this.props.short}
onClick={this.props.onChange} onClick={this.props.onChange}
className={`strong ${option.value === active.value ? 'active': ''}`} />)} className={`strong ${option.value === active.value ? 'active': ''}`} />))}
</div>); </div>);
} }
}); }
var RoundedToggleOption = React.createClass({ class RoundedToggleOption extends React.PureComponent {
mixins: [PureRenderMixin], static propTypes = {
propTypes: {
option: roundedToggleOptionType, option: roundedToggleOptionType,
className: React.PropTypes.string.isRequired, className: PropTypes.string.isRequired,
short: React.PropTypes.bool, short: PropTypes.bool,
onClick: React.PropTypes.func.isRequired onClick: PropTypes.func.isRequired
}, }
onClick() { onClick = () => {
this.props.onClick(this.props.option); this.props.onClick(this.props.option);
}, }
render() { render() {
let { className, option } = this.props; let { className, option } = this.props;
return (<a return (<a
onClick={this.onClick} onClick={this.onClick}
className={className}>{this.props.short ? option.short : option.title}</a>); className={className}>{this.props.short ? option.short : option.title}</a>);
} }
}); }
module.exports = RoundedToggle;

View File

@ -1,8 +1,8 @@
import React from 'react'; import React from 'react';
import remark from 'remark'; import remark from 'remark';
import PropTypes from 'prop-types';
import remarkHTML from 'remark-html'; import remarkHTML from 'remark-html';
import remarkHighlight from '../highlight'; import remarkHighlight from '../highlight';
import PureRenderMixin from 'react-pure-render/mixin';
import { postHighlight, remarkPlugins } from '../custom'; import { postHighlight, remarkPlugins } from '../custom';
function renderHighlighted(nodes) { function renderHighlighted(nodes) {
@ -19,13 +19,12 @@ function renderHighlighted(nodes) {
}; };
} }
var Section = React.createClass({ export default class Section extends React.PureComponent {
mixins: [PureRenderMixin], static propTypes = {
propTypes: { chunk: PropTypes.object.isRequired,
chunk: React.PropTypes.object.isRequired, leftClassname: PropTypes.string.isRequired,
leftClassname: React.PropTypes.string.isRequired, rightClassname: PropTypes.string.isRequired
rightClassname: React.PropTypes.string.isRequired }
},
render() { render() {
let { chunk, leftClassname, rightClassname } = this.props; let { chunk, leftClassname, rightClassname } = this.props;
let { left, right, preview } = chunk; let { left, right, preview } = chunk;
@ -40,6 +39,4 @@ var Section = React.createClass({
dangerouslySetInnerHTML={renderHighlighted(right)} />} dangerouslySetInnerHTML={renderHighlighted(right)} />}
</div>); </div>);
} }
}); }
module.exports = Section;