commit 543d650b7873fbc193aabd6e2a09a1c5123d543e
parent 3c3e18ec571a88a2f8430b4a2bacecf720f14b03
Author: Vladimir Kutepov <frenzzy.man@gmail.com>
Date: Thu, 10 Nov 2016 21:29:13 +0400
Use class components everywhere and optimize React code automatically (#961)
Now you do not need to repeatedly rewrite your [class components to stateless functional components](https://facebook.github.io/react/docs/components-and-props.html#functional-and-class-components) and back. Just always use `class` inherited from `React.Component` or `React.PureComponent` and [transform-react-pure-class-to-function](https://github.com/thejameskyle/babel-react-optimize) plugin will automatically optimize React code by making pure classes into functions in release mode.
Diffstat:
17 files changed, 371 insertions(+), 346 deletions(-)
diff --git a/package.json b/package.json
@@ -46,12 +46,10 @@
"babel-eslint": "^7.1.0",
"babel-loader": "^6.2.7",
"babel-plugin-rewire": "^1.0.0",
- "babel-plugin-transform-react-constant-elements": "^6.9.1",
- "babel-plugin-transform-react-inline-elements": "^6.8.0",
- "babel-plugin-transform-react-remove-prop-types": "^0.2.10",
"babel-plugin-transform-runtime": "^6.15.0",
"babel-preset-latest": "^6.16.0",
"babel-preset-react": "^6.16.0",
+ "babel-preset-react-optimize": "^1.0.1",
"babel-preset-stage-0": "^6.16.0",
"babel-register": "^6.18.0",
"babel-template": "^6.16.0",
@@ -146,7 +144,8 @@
"react/forbid-prop-types": "off",
"react/jsx-filename-extension": "off",
"react/no-danger": "off",
- "react/no-unused-prop-types": "off"
+ "react/no-unused-prop-types": "off",
+ "react/prefer-stateless-function": "off"
}
},
"stylelint": {
diff --git a/src/components/Feedback/Feedback.js b/src/components/Feedback/Feedback.js
@@ -11,22 +11,24 @@ import React from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './Feedback.css';
-function Feedback() {
- return (
- <div className={s.root}>
- <div className={s.container}>
- <a
- className={s.link}
- href="https://gitter.im/kriasoft/react-starter-kit"
- >Ask a question</a>
- <span className={s.spacer}>|</span>
- <a
- className={s.link}
- href="https://github.com/kriasoft/react-starter-kit/issues/new"
- >Report an issue</a>
+class Feedback extends React.Component {
+ render() {
+ return (
+ <div className={s.root}>
+ <div className={s.container}>
+ <a
+ className={s.link}
+ href="https://gitter.im/kriasoft/react-starter-kit"
+ >Ask a question</a>
+ <span className={s.spacer}>|</span>
+ <a
+ className={s.link}
+ href="https://github.com/kriasoft/react-starter-kit/issues/new"
+ >Report an issue</a>
+ </div>
</div>
- </div>
- );
+ );
+ }
}
export default withStyles(s)(Feedback);
diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js
@@ -12,22 +12,24 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './Footer.css';
import Link from '../Link';
-function Footer() {
- return (
- <div className={s.root}>
- <div className={s.container}>
- <span className={s.text}>© Your Company</span>
- <span className={s.spacer}>·</span>
- <Link className={s.link} to="/">Home</Link>
- <span className={s.spacer}>·</span>
- <Link className={s.link} to="/admin">Admin</Link>
- <span className={s.spacer}>·</span>
- <Link className={s.link} to="/privacy">Privacy</Link>
- <span className={s.spacer}>·</span>
- <Link className={s.link} to="/not-found">Not Found</Link>
+class Footer extends React.Component {
+ render() {
+ return (
+ <div className={s.root}>
+ <div className={s.container}>
+ <span className={s.text}>© Your Company</span>
+ <span className={s.spacer}>·</span>
+ <Link className={s.link} to="/">Home</Link>
+ <span className={s.spacer}>·</span>
+ <Link className={s.link} to="/admin">Admin</Link>
+ <span className={s.spacer}>·</span>
+ <Link className={s.link} to="/privacy">Privacy</Link>
+ <span className={s.spacer}>·</span>
+ <Link className={s.link} to="/not-found">Not Found</Link>
+ </div>
</div>
- </div>
- );
+ );
+ }
}
export default withStyles(s)(Footer);
diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js
@@ -14,22 +14,24 @@ import Link from '../Link';
import Navigation from '../Navigation';
import logoUrl from './logo-small.png';
-function Header() {
- return (
- <div className={s.root}>
- <div className={s.container}>
- <Navigation className={s.nav} />
- <Link className={s.brand} to="/">
- <img src={logoUrl} width="38" height="38" alt="React" />
- <span className={s.brandTxt}>Your Company</span>
- </Link>
- <div className={s.banner}>
- <h1 className={s.bannerTitle}>React</h1>
- <p className={s.bannerDesc}>Complex web apps made easy</p>
+class Header extends React.Component {
+ render() {
+ return (
+ <div className={s.root}>
+ <div className={s.container}>
+ <Navigation className={s.nav} />
+ <Link className={s.brand} to="/">
+ <img src={logoUrl} width="38" height="38" alt="React" />
+ <span className={s.brandTxt}>Your Company</span>
+ </Link>
+ <div className={s.banner}>
+ <h1 className={s.bannerTitle}>React</h1>
+ <p className={s.bannerDesc}>Complex web apps made easy</p>
+ </div>
</div>
</div>
- </div>
- );
+ );
+ }
}
export default withStyles(s)(Header);
diff --git a/src/components/Html.js b/src/components/Html.js
@@ -10,44 +10,47 @@
import React, { PropTypes } from 'react';
import { analytics } from '../config';
-function Html({ title, description, style, script, chunk, children }) {
- return (
- <html className="no-js" lang="en">
- <head>
- <meta charSet="utf-8" />
- <meta httpEquiv="x-ua-compatible" content="ie=edge" />
- <title>{title}</title>
- <meta name="description" content={description} />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <link rel="apple-touch-icon" href="apple-touch-icon.png" />
- {style && <style id="css" dangerouslySetInnerHTML={{ __html: style }} />}
- </head>
- <body>
- <div id="app" dangerouslySetInnerHTML={{ __html: children }} />
- {script && <script src={script} />}
- {chunk && <script src={chunk} />}
- {analytics.google.trackingId &&
- <script
- dangerouslySetInnerHTML={{ __html:
- 'window.ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date;' +
- `ga('create','${analytics.google.trackingId}','auto');ga('send','pageview')` }}
- />
- }
- {analytics.google.trackingId &&
- <script src="https://www.google-analytics.com/analytics.js" async defer />
- }
- </body>
- </html>
- );
-}
+class Html extends React.Component {
+ static propTypes = {
+ title: PropTypes.string.isRequired,
+ description: PropTypes.string.isRequired,
+ style: PropTypes.string,
+ script: PropTypes.string,
+ chunk: PropTypes.string,
+ children: PropTypes.string,
+ };
-Html.propTypes = {
- title: PropTypes.string.isRequired,
- description: PropTypes.string.isRequired,
- style: PropTypes.string,
- script: PropTypes.string,
- chunk: PropTypes.string,
- children: PropTypes.string,
-};
+ render() {
+ const { title, description, style, script, chunk, children } = this.props;
+ return (
+ <html className="no-js" lang="en">
+ <head>
+ <meta charSet="utf-8" />
+ <meta httpEquiv="x-ua-compatible" content="ie=edge" />
+ <title>{title}</title>
+ <meta name="description" content={description} />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="apple-touch-icon" href="apple-touch-icon.png" />
+ {style && <style id="css" dangerouslySetInnerHTML={{ __html: style }} />}
+ </head>
+ <body>
+ <div id="app" dangerouslySetInnerHTML={{ __html: children }} />
+ {script && <script src={script} />}
+ {chunk && <script src={chunk} />}
+ {analytics.google.trackingId &&
+ <script
+ dangerouslySetInnerHTML={{ __html:
+ 'window.ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date;' +
+ `ga('create','${analytics.google.trackingId}','auto');ga('send','pageview')` }}
+ />
+ }
+ {analytics.google.trackingId &&
+ <script src="https://www.google-analytics.com/analytics.js" async defer />
+ }
+ </body>
+ </html>
+ );
+ }
+}
export default Html;
diff --git a/src/components/Layout/Layout.js b/src/components/Layout/Layout.js
@@ -14,19 +14,21 @@ import Header from '../Header';
import Feedback from '../Feedback';
import Footer from '../Footer';
-function Layout({ children }) {
- return (
- <div>
- <Header />
- {children}
- <Feedback />
- <Footer />
- </div>
- );
-}
+class Layout extends React.Component {
+ static propTypes = {
+ children: PropTypes.node.isRequired,
+ };
-Layout.propTypes = {
- children: PropTypes.node.isRequired,
-};
+ render() {
+ return (
+ <div>
+ <Header />
+ {this.props.children}
+ <Feedback />
+ <Footer />
+ </div>
+ );
+ }
+}
export default withStyles(s)(Layout);
diff --git a/src/components/Link/Link.js b/src/components/Link/Link.js
@@ -7,7 +7,7 @@
* LICENSE.txt file in the root directory of this source tree.
*/
-import React, { Component, PropTypes } from 'react';
+import React, { PropTypes } from 'react';
import history from '../../core/history';
function isLeftClickEvent(event) {
@@ -18,8 +18,7 @@ function isModifiedEvent(event) {
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
}
-class Link extends Component {
-
+class Link extends React.Component {
static propTypes = {
to: PropTypes.string.isRequired,
children: PropTypes.node,
@@ -47,7 +46,6 @@ class Link extends Component {
const { to, children, ...props } = this.props;
return <a href={to} {...props} onClick={this.handleClick}>{children}</a>;
}
-
}
export default Link;
diff --git a/src/components/Navigation/Navigation.js b/src/components/Navigation/Navigation.js
@@ -13,21 +13,23 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './Navigation.css';
import Link from '../Link';
-function Navigation({ className }) {
- return (
- <div className={cx(s.root, className)} role="navigation">
- <Link className={s.link} to="/about">About</Link>
- <Link className={s.link} to="/contact">Contact</Link>
- <span className={s.spacer}> | </span>
- <Link className={s.link} to="/login">Log in</Link>
- <span className={s.spacer}>or</span>
- <Link className={cx(s.link, s.highlight)} to="/register">Sign up</Link>
- </div>
- );
-}
+class Navigation extends React.Component {
+ static propTypes = {
+ className: PropTypes.string,
+ };
-Navigation.propTypes = {
- className: PropTypes.string,
-};
+ render() {
+ return (
+ <div className={cx(s.root, this.props.className)} role="navigation">
+ <Link className={s.link} to="/about">About</Link>
+ <Link className={s.link} to="/contact">Contact</Link>
+ <span className={s.spacer}> | </span>
+ <Link className={s.link} to="/login">Log in</Link>
+ <span className={s.spacer}>or</span>
+ <Link className={cx(s.link, s.highlight)} to="/register">Sign up</Link>
+ </div>
+ );
+ }
+}
export default withStyles(s)(Navigation);
diff --git a/src/routes/admin/Admin.js b/src/routes/admin/Admin.js
@@ -12,21 +12,23 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles';
import Layout from '../../components/Layout';
import s from './Admin.css';
-function Admin({ title }) {
- return (
- <Layout>
- <div className={s.root}>
- <div className={s.container}>
- <h1>{title}</h1>
- <p>...</p>
+class Admin extends React.Component {
+ static propTypes = {
+ title: PropTypes.string.isRequired,
+ };
+
+ render() {
+ return (
+ <Layout>
+ <div className={s.root}>
+ <div className={s.container}>
+ <h1>{this.props.title}</h1>
+ <p>...</p>
+ </div>
</div>
- </div>
- </Layout>
- );
+ </Layout>
+ );
+ }
}
-Admin.propTypes = {
- title: PropTypes.string.isRequired,
-};
-
export default withStyles(s)(Admin);
diff --git a/src/routes/contact/Contact.js b/src/routes/contact/Contact.js
@@ -12,21 +12,23 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles';
import Layout from '../../components/Layout';
import s from './Contact.css';
-function Contact({ title }) {
- return (
- <Layout>
- <div className={s.root}>
- <div className={s.container}>
- <h1>{title}</h1>
- <p>...</p>
+class Contact extends React.Component {
+ static propTypes = {
+ title: PropTypes.string.isRequired,
+ };
+
+ render() {
+ return (
+ <Layout>
+ <div className={s.root}>
+ <div className={s.container}>
+ <h1>{this.props.title}</h1>
+ <p>...</p>
+ </div>
</div>
- </div>
- </Layout>
- );
+ </Layout>
+ );
+ }
}
-Contact.propTypes = {
- title: PropTypes.string.isRequired,
-};
-
export default withStyles(s)(Contact);
diff --git a/src/routes/content/Content.js b/src/routes/content/Content.js
@@ -12,23 +12,26 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles';
import Layout from '../../components/Layout';
import s from './Content.css';
-function Content({ path, title, content }) {
- return (
- <Layout>
- <div className={s.root}>
- <div className={s.container}>
- {title && path !== '/' && <h1>{title}</h1>}
- <div dangerouslySetInnerHTML={{ __html: content }} />
+class Content extends React.Component {
+ static propTypes = {
+ path: PropTypes.string.isRequired,
+ content: PropTypes.string.isRequired,
+ title: PropTypes.string,
+ };
+
+ render() {
+ const { path, title, content } = this.props;
+ return (
+ <Layout>
+ <div className={s.root}>
+ <div className={s.container}>
+ {title && path !== '/' && <h1>{title}</h1>}
+ <div dangerouslySetInnerHTML={{ __html: content }} />
+ </div>
</div>
- </div>
- </Layout>
- );
+ </Layout>
+ );
+ }
}
-Content.propTypes = {
- path: PropTypes.string.isRequired,
- content: PropTypes.string.isRequired,
- title: PropTypes.string,
-};
-
export default withStyles(s)(Content);
diff --git a/src/routes/error/ErrorPage.js b/src/routes/error/ErrorPage.js
@@ -11,28 +11,31 @@ import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './ErrorPage.css';
-function ErrorPage({ error }) {
- if (process.env.NODE_ENV !== 'production') {
+class ErrorPage extends React.Component {
+ static propTypes = {
+ error: PropTypes.object.isRequired,
+ };
+
+ render() {
+ if (process.env.NODE_ENV !== 'production') {
+ const { error } = this.props;
+ return (
+ <div>
+ <h1>{error.name}</h1>
+ <p>{error.message}</p>
+ <pre>{error.stack}</pre>
+ </div>
+ );
+ }
+
return (
<div>
- <h1>{error.name}</h1>
- <p>{error.message}</p>
- <pre>{error.stack}</pre>
+ <h1>Error</h1>
+ <p>Sorry, a critical error occurred on this page.</p>
</div>
);
}
-
- return (
- <div>
- <h1>Error</h1>
- <p>Sorry, a critical error occurred on this page.</p>
- </div>
- );
}
-ErrorPage.propTypes = {
- error: PropTypes.object.isRequired,
-};
-
export { ErrorPage as ErrorPageWithoutStyle };
export default withStyles(s)(ErrorPage);
diff --git a/src/routes/home/Home.js b/src/routes/home/Home.js
@@ -12,35 +12,37 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles';
import Layout from '../../components/Layout';
import s from './Home.css';
-function Home({ news }) {
- return (
- <Layout>
- <div className={s.root}>
- <div className={s.container}>
- <h1 className={s.title}>React.js News</h1>
- <ul className={s.news}>
- {news.map((item, index) => (
- <li key={index} className={s.newsItem}>
- <a href={item.link} className={s.newsTitle}>{item.title}</a>
- <span
- className={s.newsDesc}
- dangerouslySetInnerHTML={{ __html: item.contentSnippet }}
- />
- </li>
- ))}
- </ul>
+class Home extends React.Component {
+ static propTypes = {
+ news: PropTypes.arrayOf(PropTypes.shape({
+ title: PropTypes.string.isRequired,
+ link: PropTypes.string.isRequired,
+ contentSnippet: PropTypes.string,
+ })).isRequired,
+ };
+
+ render() {
+ return (
+ <Layout>
+ <div className={s.root}>
+ <div className={s.container}>
+ <h1 className={s.title}>React.js News</h1>
+ <ul className={s.news}>
+ {this.props.news.map((item, index) => (
+ <li key={index} className={s.newsItem}>
+ <a href={item.link} className={s.newsTitle}>{item.title}</a>
+ <span
+ className={s.newsDesc}
+ dangerouslySetInnerHTML={{ __html: item.contentSnippet }}
+ />
+ </li>
+ ))}
+ </ul>
+ </div>
</div>
- </div>
- </Layout>
- );
+ </Layout>
+ );
+ }
}
-Home.propTypes = {
- news: PropTypes.arrayOf(PropTypes.shape({
- title: PropTypes.string.isRequired,
- link: PropTypes.string.isRequired,
- contentSnippet: PropTypes.string,
- })).isRequired,
-};
-
export default withStyles(s)(Home);
diff --git a/src/routes/login/Login.js b/src/routes/login/Login.js
@@ -12,113 +12,115 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles';
import Layout from '../../components/Layout';
import s from './Login.css';
-function Login({ title }) {
- return (
- <Layout>
- <div className={s.root}>
- <div className={s.container}>
- <h1>{title}</h1>
- <p className={s.lead}>Log in with your username or company email address.</p>
- <div className={s.formGroup}>
- <a className={s.facebook} href="/login/facebook">
- <svg
- className={s.icon}
- width="30"
- height="30"
- viewBox="0 0 30 30"
- xmlns="http://www.w3.org/2000/svg"
- >
- <path
- d="M22 16l1-5h-5V7c0-1.544.784-2 3-2h2V0h-4c-4.072 0-7 2.435-7 7v4H7v5h5v14h6V16h4z"
- />
- </svg>
- <span>Log in with Facebook</span>
- </a>
- </div>
- <div className={s.formGroup}>
- <a className={s.google} href="/login/google">
- <svg
- className={s.icon}
- width="30"
- height="30"
- viewBox="0 0 30 30"
- xmlns="http://www.w3.org/2000/svg"
- >
- <path
- d={'M30 13h-4V9h-2v4h-4v2h4v4h2v-4h4m-15 2s-2-1.15-2-2c0 0-.5-1.828 1-3 ' +
- '1.537-1.2 3-3.035 3-5 0-2.336-1.046-5-3-6h3l2.387-1H10C5.835 0 2 3.345 2 7c0 ' +
- '3.735 2.85 6.56 7.086 6.56.295 0 .58-.006.86-.025-.273.526-.47 1.12-.47 1.735 ' +
- '0 1.037.817 2.042 1.523 2.73H9c-5.16 0-9 2.593-9 6 0 3.355 4.87 6 10.03 6 5.882 ' +
- '0 9.97-3 9.97-7 0-2.69-2.545-4.264-5-6zm-4-4c-2.395 0-5.587-2.857-6-6C4.587 ' +
- '3.856 6.607.93 9 1c2.394.07 4.603 2.908 5.017 6.052C14.43 10.195 13 13 11 ' +
- '13zm-1 15c-3.566 0-7-1.29-7-4 0-2.658 3.434-5.038 7-5 .832.01 2 0 2 0 1 0 ' +
- '2.88.88 4 2 1 1 1 2.674 1 3 0 3-1.986 4-7 4z'}
- />
- </svg>
- <span>Log in with Google</span>
- </a>
- </div>
- <div className={s.formGroup}>
- <a className={s.twitter} href="/login/twitter">
- <svg
- className={s.icon}
- width="30"
- height="30"
- viewBox="0 0 30 30"
- xmlns="http://www.w3.org/2000/svg"
- >
- <path
- d={'M30 6.708c-1.105.49-2.756 1.143-4 1.292 1.273-.762 2.54-2.56 ' +
- '3-4-.97.577-2.087 1.355-3.227 1.773L25 5c-1.12-1.197-2.23-2-4-2-3.398 0-6 ' +
- '2.602-6 6 0 .4.047.7.11.956L15 10C9 10 5.034 8.724 2 5c-.53.908-1 1.872-1 ' +
- '3 0 2.136 1.348 3.894 3 5-1.01-.033-2.17-.542-3-1 0 2.98 4.186 6.432 7 7-1 ' +
- '1-4.623.074-5 0 .784 2.447 3.31 3.95 6 4-2.105 1.648-4.647 2.51-7.53 2.51-.5 ' +
- '0-.988-.03-1.47-.084C2.723 27.17 6.523 28 10 28c11.322 0 17-8.867 17-17 ' +
- '0-.268.008-.736 0-1 1.2-.868 2.172-2.058 3-3.292z'}
- />
- </svg>
- <span>Log in with Twitter</span>
- </a>
- </div>
- <strong className={s.lineThrough}>OR</strong>
- <form method="post">
+class Login extends React.Component {
+ static propTypes = {
+ title: PropTypes.string.isRequired,
+ };
+
+ render() {
+ return (
+ <Layout>
+ <div className={s.root}>
+ <div className={s.container}>
+ <h1>{this.props.title}</h1>
+ <p className={s.lead}>Log in with your username or company email address.</p>
<div className={s.formGroup}>
- <label className={s.label} htmlFor="usernameOrEmail">
- Username or email address:
- </label>
- <input
- className={s.input}
- id="usernameOrEmail"
- type="text"
- name="usernameOrEmail"
- autoFocus
- />
+ <a className={s.facebook} href="/login/facebook">
+ <svg
+ className={s.icon}
+ width="30"
+ height="30"
+ viewBox="0 0 30 30"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M22 16l1-5h-5V7c0-1.544.784-2 3-2h2V0h-4c-4.072 0-7 2.435-7 7v4H7v5h5v14h6V16h4z"
+ />
+ </svg>
+ <span>Log in with Facebook</span>
+ </a>
</div>
<div className={s.formGroup}>
- <label className={s.label} htmlFor="password">
- Password:
- </label>
- <input
- className={s.input}
- id="password"
- type="password"
- name="password"
- />
+ <a className={s.google} href="/login/google">
+ <svg
+ className={s.icon}
+ width="30"
+ height="30"
+ viewBox="0 0 30 30"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d={'M30 13h-4V9h-2v4h-4v2h4v4h2v-4h4m-15 2s-2-1.15-2-2c0 0-.5-1.828 1-3 ' +
+ '1.537-1.2 3-3.035 3-5 0-2.336-1.046-5-3-6h3l2.387-1H10C5.835 0 2 3.345 2 7c0 ' +
+ '3.735 2.85 6.56 7.086 6.56.295 0 .58-.006.86-.025-.273.526-.47 1.12-.47 1.735 ' +
+ '0 1.037.817 2.042 1.523 2.73H9c-5.16 0-9 2.593-9 6 0 3.355 4.87 6 10.03 6 5.882 ' +
+ '0 9.97-3 9.97-7 0-2.69-2.545-4.264-5-6zm-4-4c-2.395 0-5.587-2.857-6-6C4.587 ' +
+ '3.856 6.607.93 9 1c2.394.07 4.603 2.908 5.017 6.052C14.43 10.195 13 13 11 ' +
+ '13zm-1 15c-3.566 0-7-1.29-7-4 0-2.658 3.434-5.038 7-5 .832.01 2 0 2 0 1 0 ' +
+ '2.88.88 4 2 1 1 1 2.674 1 3 0 3-1.986 4-7 4z'}
+ />
+ </svg>
+ <span>Log in with Google</span>
+ </a>
</div>
<div className={s.formGroup}>
- <button className={s.button} type="submit">
- Log in
- </button>
+ <a className={s.twitter} href="/login/twitter">
+ <svg
+ className={s.icon}
+ width="30"
+ height="30"
+ viewBox="0 0 30 30"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d={'M30 6.708c-1.105.49-2.756 1.143-4 1.292 1.273-.762 2.54-2.56 ' +
+ '3-4-.97.577-2.087 1.355-3.227 1.773L25 5c-1.12-1.197-2.23-2-4-2-3.398 0-6 ' +
+ '2.602-6 6 0 .4.047.7.11.956L15 10C9 10 5.034 8.724 2 5c-.53.908-1 1.872-1 ' +
+ '3 0 2.136 1.348 3.894 3 5-1.01-.033-2.17-.542-3-1 0 2.98 4.186 6.432 7 7-1 ' +
+ '1-4.623.074-5 0 .784 2.447 3.31 3.95 6 4-2.105 1.648-4.647 2.51-7.53 2.51-.5 ' +
+ '0-.988-.03-1.47-.084C2.723 27.17 6.523 28 10 28c11.322 0 17-8.867 17-17 ' +
+ '0-.268.008-.736 0-1 1.2-.868 2.172-2.058 3-3.292z'}
+ />
+ </svg>
+ <span>Log in with Twitter</span>
+ </a>
</div>
- </form>
+ <strong className={s.lineThrough}>OR</strong>
+ <form method="post">
+ <div className={s.formGroup}>
+ <label className={s.label} htmlFor="usernameOrEmail">
+ Username or email address:
+ </label>
+ <input
+ className={s.input}
+ id="usernameOrEmail"
+ type="text"
+ name="usernameOrEmail"
+ autoFocus
+ />
+ </div>
+ <div className={s.formGroup}>
+ <label className={s.label} htmlFor="password">
+ Password:
+ </label>
+ <input
+ className={s.input}
+ id="password"
+ type="password"
+ name="password"
+ />
+ </div>
+ <div className={s.formGroup}>
+ <button className={s.button} type="submit">
+ Log in
+ </button>
+ </div>
+ </form>
+ </div>
</div>
- </div>
- </Layout>
- );
+ </Layout>
+ );
+ }
}
-Login.propTypes = {
- title: PropTypes.string.isRequired,
-};
-
export default withStyles(s)(Login);
diff --git a/src/routes/notFound/NotFound.js b/src/routes/notFound/NotFound.js
@@ -12,21 +12,23 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles';
import Layout from '../../components/Layout';
import s from './NotFound.css';
-function NotFound({ title }) {
- return (
- <Layout full={false}>
- <div className={s.root}>
- <div className={s.container}>
- <h1>{title}</h1>
- <p>Sorry, the page you were trying to view does not exist.</p>
+class NotFound extends React.Component {
+ static propTypes = {
+ title: PropTypes.string.isRequired,
+ };
+
+ render() {
+ return (
+ <Layout full={false}>
+ <div className={s.root}>
+ <div className={s.container}>
+ <h1>{this.props.title}</h1>
+ <p>Sorry, the page you were trying to view does not exist.</p>
+ </div>
</div>
- </div>
- </Layout>
- );
+ </Layout>
+ );
+ }
}
-NotFound.propTypes = {
- title: PropTypes.string.isRequired,
-};
-
export default withStyles(s)(NotFound);
diff --git a/src/routes/register/Register.js b/src/routes/register/Register.js
@@ -12,19 +12,23 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles';
import Layout from '../../components/Layout';
import s from './Register.css';
-function Register({ title }) {
- return (
- <Layout>
- <div className={s.root}>
- <div className={s.container}>
- <h1>{title}</h1>
- <p>...</p>
+class Register extends React.Component {
+ static propTypes = {
+ title: PropTypes.string.isRequired,
+ };
+
+ render() {
+ return (
+ <Layout>
+ <div className={s.root}>
+ <div className={s.container}>
+ <h1>{this.props.title}</h1>
+ <p>...</p>
+ </div>
</div>
- </div>
- </Layout>
- );
+ </Layout>
+ );
+ }
}
-Register.propTypes = { title: PropTypes.string.isRequired };
-
export default withStyles(s)(Register);
diff --git a/tools/webpack.config.js b/tools/webpack.config.js
@@ -44,38 +44,33 @@ const config = {
// https://babeljs.io/docs/usage/options/
babelrc: false,
presets: [
- // JSX, Flow
- // https://github.com/babel/babel/tree/master/packages/babel-preset-react
- 'react',
// Latest stable ECMAScript features
// https://github.com/babel/babel/tree/master/packages/babel-preset-latest
'latest',
// Experimental ECMAScript proposals
// https://github.com/babel/babel/tree/master/packages/babel-preset-stage-0
'stage-0',
+ // JSX, Flow
+ // https://github.com/babel/babel/tree/master/packages/babel-preset-react
+ 'react',
+ ...isDebug ? [] : [
+ // Optimize React code for the production build
+ // https://github.com/thejameskyle/babel-react-optimize
+ 'react-optimize',
+ ],
],
plugins: [
// Externalise references to helpers and builtins,
// automatically polyfilling your code without polluting globals.
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-runtime
'transform-runtime',
- ...isDebug ? [
+ ...!isDebug ? [] : [
// Adds component stack to warning messages
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-source
'transform-react-jsx-source',
// Adds __self attribute to JSX which React will use for some warnings
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-self
'transform-react-jsx-self',
- ] : [
- // Remove unnecessary React propTypes from the production build
- // https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types
- 'transform-react-remove-prop-types',
- // Treat React JSX elements as value types and hoist them to the highest scope
- // https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-constant-elements
- 'transform-react-constant-elements',
- // Turn JSX elements into exploded React objects
- // https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-inline-elements
- 'transform-react-inline-elements',
],
],
},