commit 7e06940b0616b8f3d9e84faeb3b533f4854d6a72
parent ee0d700833c17c3c6b9659ca555c96a3d8ab6c3f
Author: Konstantin Tarkus <hello@tarkus.me>
Date: Sun, 15 Mar 2015 20:35:53 +0300
Update React from 0.12.2 to 0.13.0; update React components to ES classes
Diffstat:
15 files changed, 161 insertions(+), 201 deletions(-)
diff --git a/package.json b/package.json
@@ -10,41 +10,41 @@
"npm": ">= 2.1"
},
"dependencies": {
- "babel": "4.7.1",
+ "babel": "4.7.12",
"bootstrap": "3.3.2",
"eventemitter3": "0.1.6",
"express": "4.12.2",
"flux": "2.0.1",
"front-matter": "1.0.0",
"jade": "1.9.2",
- "lodash": "3.4.0",
- "react": "0.12.2",
- "superagent": "0.21.0"
+ "lodash": "3.5.0",
+ "react": "0.13.0",
+ "superagent": "1.1.0"
},
"devDependencies": {
"autoprefixer-loader": "^1.2.0",
- "babel-core": "^4.7.1",
- "babel-eslint": "^1.0.13",
+ "babel-core": "^4.7.12",
+ "babel-eslint": "^2.0.0",
"babel-loader": "^4.1.0",
- "browser-sync": "^2.2.2",
+ "browser-sync": "^2.2.4",
"css-loader": "^0.9.1",
"del": "^1.1.1",
- "eslint": "^0.15.1",
- "eslint-loader": "^0.4.0",
+ "eslint": "^0.16.0",
+ "eslint-loader": "^0.6.0",
"gulp": "^3.8.11",
"gulp-autoprefixer": "^2.1.0",
"gulp-cache": "^0.2.8",
- "gulp-changed": "^1.1.1",
+ "gulp-changed": "^1.2.1",
"gulp-csscomb": "^3.0.3",
- "gulp-gh-pages": "^0.4.0",
+ "gulp-gh-pages": "^0.5.0",
"gulp-htmlmin": "^1.1.1",
"gulp-if": "^1.2.5",
"gulp-imagemin": "^2.2.1",
- "gulp-jsbeautifier": "^0.0.5",
+ "gulp-jsbeautifier": "^0.0.6",
"gulp-less": "^3.0.1",
"gulp-load-plugins": "^0.8.1",
- "gulp-minify-css": "^0.5.1",
- "gulp-plumber": "^0.6.6",
+ "gulp-minify-css": "^1.0.0",
+ "gulp-plumber": "^1.0.0",
"gulp-rename": "^1.2.0",
"gulp-render": "^0.2.2",
"gulp-replace": "^0.5.3",
@@ -54,14 +54,14 @@
"jest-cli": "^0.4.0",
"less": "^2.4.0",
"less-loader": "^2.1.0",
- "minimist": "^1.1.0",
+ "minimist": "^1.1.1",
"protractor": "^1.8.0",
"psi": "^1.0.6",
- "react-tools": "^0.12.2",
+ "react-tools": "^0.13.0",
"run-sequence": "^1.0.2",
"style-loader": "^0.8.3",
"url-loader": "^0.5.5",
- "webpack": "^1.7.2",
+ "webpack": "^1.7.3",
"webpack-dev-server": "^1.7.0"
},
"jest": {
diff --git a/src/actions/AppActions.js b/src/actions/AppActions.js
@@ -13,28 +13,37 @@ import ActionTypes from '../constants/ActionTypes';
import ExecutionEnvironment from 'react/lib/ExecutionEnvironment';
import http from 'superagent';
-module.exports = {
+export default {
- navigateTo(path) {
+ navigateTo(path, options) {
if (ExecutionEnvironment.canUseDOM) {
- window.history.pushState({}, document.title, path);
+ if (options && options.replace) {
+ window.history.replaceState({}, document.title, path);
+ } else {
+ window.history.pushState({}, document.title, path);
+ }
}
Dispatcher.handleViewAction({
- actionType: ActionTypes.CHANGE_LOCATION, path: path
+ actionType: ActionTypes.CHANGE_LOCATION,
+ path
});
},
loadPage(path, cb) {
Dispatcher.handleViewAction({
- actionType: ActionTypes.LOAD_PAGE, path: path
+ actionType: ActionTypes.LOAD_PAGE,
+ path
});
http.get('/api/page' + path)
.accept('application/json')
.end((err, res) => {
Dispatcher.handleServerAction({
- actionType: ActionTypes.LOAD_PAGE, path: path, err: err, page: res.body
+ actionType: ActionTypes.LOAD_PAGE,
+ path,
+ err,
+ page: res.body
});
if (cb) {
cb();
diff --git a/src/app.js b/src/app.js
@@ -10,24 +10,24 @@
import 'babel/polyfill';
-import React from 'react';
+import React from 'react/addons';
import emptyFunction from 'react/lib/emptyFunction';
import App from './components/App';
import Dispatcher from './core/Dispatcher';
import AppActions from './actions/AppActions';
import ActionTypes from './constants/ActionTypes';
-var path = decodeURI(window.location.pathname);
-var setMetaTag = (name, content) => {
+let path = decodeURI(window.location.pathname);
+let setMetaTag = (name, content) => {
// Remove and create a new <meta /> tag in order to make it work
// with bookmarks in Safari
- var elements = document.getElementsByTagName('meta');
+ let elements = document.getElementsByTagName('meta');
[].slice.call(elements).forEach((element) => {
if (element.getAttribute('name') === name) {
element.parentNode.removeChild(element);
}
});
- var meta = document.createElement('meta');
+ let meta = document.createElement('meta');
meta.setAttribute('name', name);
meta.setAttribute('content', content);
document.getElementsByTagName('head')[0].appendChild(meta);
@@ -35,19 +35,20 @@ var setMetaTag = (name, content) => {
function run() {
// Render the top-level React component
- var props = {
+ let props = {
path: path,
onSetTitle: (title) => document.title = title,
onSetMeta: setMetaTag,
onPageNotFound: emptyFunction
};
- var component = React.createElement(App, props);
- var app = React.render(component, document.body);
+ let element = React.createElement(App, props);
+ React.render(element, document.body);
// Update `Application.path` prop when `window.location` is changed
Dispatcher.register((payload) => {
if (payload.action.actionType === ActionTypes.CHANGE_LOCATION) {
- app.setProps({path: decodeURI(payload.action.path)});
+ element = React.cloneElement(element, {path: payload.action.path});
+ React.render(element, document.body);
}
});
}
diff --git a/src/components/App/App.js b/src/components/App/App.js
@@ -12,22 +12,27 @@ import './App.less';
import React from 'react';
import invariant from 'react/lib/invariant';
-import NavigationMixin from './NavigationMixin';
+import AppActions from '../../actions/AppActions';
import AppStore from '../../stores/AppStore';
import Navbar from '../Navbar';
import ContentPage from '../ContentPage';
import NotFoundPage from '../NotFoundPage';
-export default React.createClass({
+export default class App extends React.Component {
- mixins: [NavigationMixin],
+ componentDidMount() {
+ window.addEventListener('popstate', this.handlePopState);
+ window.addEventListener('click', this.handleClick);
+ }
+
+ componentWillUnmount() {
+ window.removeEventListener('popstate', this.handlePopState);
+ window.removeEventListener('click', this.handleClick);
+ }
- propTypes: {
- path: React.PropTypes.string.isRequired,
- onSetTitle: React.PropTypes.func.isRequired,
- onSetMeta: React.PropTypes.func.isRequired,
- onPageNotFound: React.PropTypes.func.isRequired
- },
+ shouldComponentUpdate(nextProps) {
+ return this.props.path !== nextProps.path;
+ }
render() {
var page = AppStore.getPage(this.props.path);
@@ -58,7 +63,7 @@ export default React.createClass({
<div className="navbar-footer">
<div className="container">
<p className="text-muted">
- <span>© KriaSoft</span>
+ <span>© Your Company</span>
<span><a href="/">Home</a></span>
<span><a href="/privacy">Privacy</a></span>
</p>
@@ -68,4 +73,68 @@ export default React.createClass({
);
}
-});
+ handlePopState(event) {
+ AppActions.navigateTo(window.location.pathname, {replace: !!event.state});
+ }
+
+ handleClick(event) {
+ if (event.button === 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.defaultPrevented) {
+ return;
+ }
+
+ // Ensure link
+ var el = event.target;
+ while (el && el.nodeName !== 'A') {
+ el = el.parentNode;
+ }
+ if (!el || el.nodeName !== 'A') {
+ return;
+ }
+
+ // Ignore if tag has
+ // 1. "download" attribute
+ // 2. rel="external" attribute
+ if (el.getAttribute('download') || el.getAttribute('rel') === 'external') {
+ return;
+ }
+
+ // Ensure non-hash for the same path
+ var link = el.getAttribute('href');
+ if (el.pathname === location.pathname && (el.hash || link === '#')) {
+ return;
+ }
+
+ // Check for mailto: in the href
+ if (link && link.indexOf('mailto:') > -1) {
+ return;
+ }
+
+ // Check target
+ if (el.target) {
+ return;
+ }
+
+ // X-origin
+ var origin = window.location.protocol + '//' + window.location.hostname +
+ (window.location.port ? ':' + window.location.port : '');
+ if (!(el.href && el.href.indexOf(origin) === 0)) {
+ return;
+ }
+
+ // Rebuild path
+ var path = el.pathname + el.search + (el.hash || '');
+
+ event.preventDefault();
+ AppActions.loadPage(path, () => {
+ AppActions.navigateTo(path);
+ });
+ }
+
+}
+
+App.propTypes = {
+ path: React.PropTypes.string.isRequired,
+ onSetTitle: React.PropTypes.func.isRequired,
+ onSetMeta: React.PropTypes.func.isRequired,
+ onPageNotFound: React.PropTypes.func.isRequired
+};
diff --git a/src/components/App/NavigationMixin.js b/src/components/App/NavigationMixin.js
@@ -1,94 +0,0 @@
-/*
- * React.js Starter Kit
- * Copyright (c) 2014 Konstantin Tarkus (@koistya), KriaSoft LLC.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-'use strict';
-
-import ExecutionEnvironment from 'react/lib/ExecutionEnvironment';
-import AppActions from '../../actions/AppActions';
-
-var NavigationMixin = {
-
- componentDidMount() {
- if (ExecutionEnvironment.canUseDOM) {
- window.addEventListener('popstate', this.handlePopState);
- window.addEventListener('click', this.handleClick);
- }
- },
-
- componentWillUnmount() {
- window.removeEventListener('popstate', this.handlePopState);
- window.removeEventListener('click', this.handleClick);
- },
-
- handlePopState(event) {
- //if (event.state) {
- // TODO: Replace current location
- // var path = event.state.path;
- // replace(path, event.state);
- //}
- if (!event.state) {
- AppActions.navigateTo(window.location.pathname);
- }
- },
-
- handleClick(event) {
- if (event.button === 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.defaultPrevented) {
- return;
- }
-
- // Ensure link
- var el = event.target;
- while (el && el.nodeName !== 'A') {
- el = el.parentNode;
- }
- if (!el || el.nodeName !== 'A') {
- return;
- }
-
- // Ignore if tag has
- // 1. "download" attribute
- // 2. rel="external" attribute
- if (el.getAttribute('download') || el.getAttribute('rel') === 'external') {
- return;
- }
-
- // Ensure non-hash for the same path
- var link = el.getAttribute('href');
- if (el.pathname === location.pathname && (el.hash || link === '#')) {
- return;
- }
-
- // Check for mailto: in the href
- if (link && link.indexOf('mailto:') > -1) {
- return;
- }
-
- // Check target
- if (el.target) {
- return;
- }
-
- // X-origin
- var origin = window.location.protocol + '//' + window.location.hostname +
- (window.location.port ? ':' + window.location.port : '');
- if (!(el.href && el.href.indexOf(origin) === 0)) {
- return;
- }
-
- // Rebuild path
- var path = el.pathname + el.search + (el.hash || '');
-
- event.preventDefault();
- AppActions.loadPage(path, () => {
- AppActions.navigateTo(path);
- });
- }
-
-};
-
-module.exports = NavigationMixin;
diff --git a/src/components/ContentPage/ContentPage.js b/src/components/ContentPage/ContentPage.js
@@ -10,17 +10,19 @@
import React from 'react';
-export default React.createClass({
-
- propTypes: {
- body: React.PropTypes.string.isRequired
- },
+export default class ContentPage extends React.Component {
render() {
var { className, body, other } = this.props;
- return <div className={'ContentPage ' + className}
- dangerouslySetInnerHTML={{__html: body}} {...other} />;
+ return (
+ <div className={'ContentPage ' + className}
+ dangerouslySetInnerHTML={{__html: body}} {...other} />
+ );
}
-});
+}
+
+ContentPage.propTypes = {
+ body: React.PropTypes.string.isRequired
+};
diff --git a/src/components/HomePage/HomePage.js b/src/components/HomePage/HomePage.js
@@ -10,15 +10,17 @@
import React from 'react';
-export default React.createClass({
-
- propTypes: {
- body: React.PropTypes.string.isRequired
- },
+export default class HomePage extends React.Component {
render() {
- return <div className="ContentPage"
- dangerouslySetInnerHTML={{__html: this.props.body}} />;
+ return (
+ <div className="ContentPage"
+ dangerouslySetInnerHTML={{__html: this.props.body}} />
+ );
}
-});
+}
+
+HomePage.propTypes = {
+ body: React.PropTypes.string.isRequired
+};
diff --git a/src/components/Navbar/Navbar.js b/src/components/Navbar/Navbar.js
@@ -10,7 +10,7 @@
import React from 'react';
-export default React.createClass({
+export default class Navbar extends React.Component {
render() {
return (
@@ -25,4 +25,4 @@ export default React.createClass({
);
}
-});
+}
diff --git a/src/components/NotFoundPage/NotFoundPage.js b/src/components/NotFoundPage/NotFoundPage.js
@@ -12,7 +12,7 @@
import React from 'react';
-export default React.createClass({
+export default class NotFoundPage extends React.Component {
render() {
return (
@@ -23,4 +23,4 @@ export default React.createClass({
);
}
-});
+}
diff --git a/src/components/TextBox/TextBox.js b/src/components/TextBox/TextBox.js
@@ -12,17 +12,7 @@ import './TextBox.less';
import React from 'react';
-export default React.createClass({
-
- propTypes: {
- maxLines: React.PropTypes.number
- },
-
- getDefaultProps() {
- return {
- maxLines: 1
- };
- },
+export default class TextBox extends React.Component {
render() {
return (
@@ -34,4 +24,12 @@ export default React.createClass({
);
}
-});
+}
+
+TextBox.propTypes = {
+ maxLines: React.PropTypes.number
+};
+
+TextBox.defaultProps = {
+ maxLines: 1
+};
diff --git a/src/constants/ActionTypes.js b/src/constants/ActionTypes.js
@@ -10,7 +10,7 @@
import keyMirror from 'react/lib/keyMirror';
-var ActionTypes = keyMirror({
+export default keyMirror({
LOAD_PAGE: null,
LOAD_PAGE_SUCCESS: null,
@@ -18,5 +18,3 @@ var ActionTypes = keyMirror({
CHANGE_LOCATION: null
});
-
-module.exports = ActionTypes;
diff --git a/src/constants/PayloadSources.js b/src/constants/PayloadSources.js
@@ -10,11 +10,9 @@
import keyMirror from 'react/lib/keyMirror';
-var PayloadSources = keyMirror({
+export default keyMirror({
VIEW_ACTION: null,
SERVER_ACTION: null
});
-
-module.exports = PayloadSources;
diff --git a/src/constants/Settings.js b/src/constants/Settings.js
@@ -1,23 +0,0 @@
-/*
- * React.js Starter Kit
- * Copyright (c) 2014 Konstantin Tarkus (@koistya), KriaSoft LLC.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-'use strict';
-
-module.exports = {
-
- defaults: {
-
- page: {
- title: 'React.js Starter Kit',
- description: 'A skeleton for an isomorphic web application (SPA) built with React.js and Flux',
- keywords: null
-
- }
- }
-
-};
diff --git a/src/core/Dispatcher.js b/src/core/Dispatcher.js
@@ -16,7 +16,7 @@ import assign from 'react/lib/Object.assign';
* A singleton that operates as the central hub for application updates.
* For more information visit https://facebook.github.io/flux/
*/
-var Dispatcher = assign(new Flux.Dispatcher(), {
+let Dispatcher = assign(new Flux.Dispatcher(), {
/**
* @param {object} action The details of the action, including the action's
@@ -44,4 +44,4 @@ var Dispatcher = assign(new Flux.Dispatcher(), {
});
-module.exports = Dispatcher;
+export default Dispatcher;
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js
@@ -96,4 +96,4 @@ AppStore.dispatcherToken = Dispatcher.register((payload) => {
});
-module.exports = AppStore;
+export default AppStore;