commit 4495f49df732c78e46347b8df5aaed27b96a302f
parent 543d650b7873fbc193aabd6e2a09a1c5123d543e
Author: Konstantin Tarkus <koistya@gmail.com>
Date: Thu, 10 Nov 2016 21:51:33 +0300
Move Layout component to routes (#959)
Unfortunately, in the current version of React, changing the top-level route to another type (even if the child UI tree is almost the same) will result in React tearing down that tree and building a new one. With this fix, the type of the parent component for each page will stay the same as a user transitions from one page/screen to another. Fixes #960.
Diffstat:
17 files changed, 161 insertions(+), 168 deletions(-)
diff --git a/package.json b/package.json
@@ -32,7 +32,7 @@
"query-string": "4.2.3",
"react": "15.3.2",
"react-dom": "15.3.2",
- "sequelize": "3.24.8",
+ "sequelize": "3.25.0",
"source-map-support": "0.4.6",
"sqlite3": "3.1.8",
"universal-router": "2.0.0",
@@ -40,7 +40,7 @@
},
"devDependencies": {
"assets-webpack-plugin": "^3.5.0",
- "autoprefixer": "^6.5.2",
+ "autoprefixer": "^6.5.3",
"babel-cli": "^6.18.0",
"babel-core": "^6.18.2",
"babel-eslint": "^7.1.0",
@@ -57,7 +57,7 @@
"browser-sync": "^2.17.5",
"chai": "^3.5.0",
"css-loader": "^0.25.0",
- "enzyme": "^2.5.1",
+ "enzyme": "^2.6.0",
"eslint": "^3.9.1",
"eslint-config-airbnb": "^13.0.0",
"eslint-loader": "^1.6.1",
@@ -81,7 +81,7 @@
"postcss-custom-properties": "^5.0.1",
"postcss-custom-selectors": "^3.0.0",
"postcss-flexbugs-fixes": "^2.0.0",
- "postcss-loader": "^1.1.0",
+ "postcss-loader": "^1.1.1",
"postcss-media-minmax": "^2.1.2",
"postcss-nested": "^1.0.0",
"postcss-nesting": "^2.3.1",
@@ -94,7 +94,7 @@
"react-addons-test-utils": "15.3.2",
"react-deep-force-update": "^2.0.1",
"react-hot-loader": "^3.0.0-beta.6",
- "redbox-react": "^1.3.2",
+ "redbox-react": "^1.3.3",
"rimraf": "^2.5.4",
"sinon": "^2.0.0-pre.3",
"stylelint": "^7.5.0",
diff --git a/src/components/App.js b/src/components/App.js
@@ -28,9 +28,16 @@ const ContextType = {
* store: createStore(),
* };
*
- * ReactDOM.render(<App context={context}><HomePage /></App>, container);
+ * ReactDOM.render(
+ * <App context={context}>
+ * <Layout>
+ * <LandingPage />
+ * </Layout>
+ * </App>,
+ * container,
+ * );
*/
-class App extends React.Component {
+class App extends React.PureComponent {
static propTypes = {
context: PropTypes.shape(ContextType).isRequired,
diff --git a/src/routes/admin/Admin.js b/src/routes/admin/Admin.js
@@ -9,7 +9,6 @@
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import Layout from '../../components/Layout';
import s from './Admin.css';
class Admin extends React.Component {
@@ -19,14 +18,12 @@ class Admin extends React.Component {
render() {
return (
- <Layout>
- <div className={s.root}>
- <div className={s.container}>
- <h1>{this.props.title}</h1>
- <p>...</p>
- </div>
+ <div className={s.root}>
+ <div className={s.container}>
+ <h1>{this.props.title}</h1>
+ <p>...</p>
</div>
- </Layout>
+ </div>
);
}
}
diff --git a/src/routes/admin/index.js b/src/routes/admin/index.js
@@ -8,6 +8,7 @@
*/
import React from 'react';
+import Layout from '../../components/Layout';
const title = 'Admin Page';
const isAdmin = false;
@@ -28,7 +29,7 @@ export default {
return {
title,
chunk: 'admin',
- component: <Admin title={title} />,
+ component: <Layout><Admin title={title} /></Layout>,
};
},
diff --git a/src/routes/contact/Contact.js b/src/routes/contact/Contact.js
@@ -9,7 +9,6 @@
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import Layout from '../../components/Layout';
import s from './Contact.css';
class Contact extends React.Component {
@@ -19,14 +18,12 @@ class Contact extends React.Component {
render() {
return (
- <Layout>
- <div className={s.root}>
- <div className={s.container}>
- <h1>{this.props.title}</h1>
- <p>...</p>
- </div>
+ <div className={s.root}>
+ <div className={s.container}>
+ <h1>{this.props.title}</h1>
+ <p>...</p>
</div>
- </Layout>
+ </div>
);
}
}
diff --git a/src/routes/contact/index.js b/src/routes/contact/index.js
@@ -8,6 +8,7 @@
*/
import React from 'react';
+import Layout from '../../components/Layout';
import Contact from './Contact';
const title = 'Contact Us';
@@ -19,7 +20,7 @@ export default {
action() {
return {
title,
- component: <Contact title={title} />,
+ component: <Layout><Contact title={title} /></Layout>,
};
},
diff --git a/src/routes/content/Content.js b/src/routes/content/Content.js
@@ -9,7 +9,6 @@
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import Layout from '../../components/Layout';
import s from './Content.css';
class Content extends React.Component {
@@ -22,14 +21,12 @@ class Content extends React.Component {
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 className={s.root}>
+ <div className={s.container}>
+ {title && path !== '/' && <h1>{title}</h1>}
+ <div dangerouslySetInnerHTML={{ __html: content }} />
</div>
- </Layout>
+ </div>
);
}
}
diff --git a/src/routes/content/index.js b/src/routes/content/index.js
@@ -8,8 +8,9 @@
*/
import React from 'react';
-import Content from './Content';
import fetch from '../../core/fetch';
+import Layout from '../../components/Layout';
+import Content from './Content';
export default {
@@ -32,7 +33,7 @@ export default {
if (!data || !data.content) return undefined;
return {
title: data.content.title,
- component: <Content {...data.content} />,
+ component: <Layout><Content {...data.content} /></Layout>,
};
},
diff --git a/src/routes/home/Home.js b/src/routes/home/Home.js
@@ -9,7 +9,6 @@
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import Layout from '../../components/Layout';
import s from './Home.css';
class Home extends React.Component {
@@ -23,24 +22,22 @@ class Home extends React.Component {
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 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>
- </Layout>
+ </div>
);
}
}
diff --git a/src/routes/home/index.js b/src/routes/home/index.js
@@ -10,6 +10,7 @@
import React from 'react';
import Home from './Home';
import fetch from '../../core/fetch';
+import Layout from '../../components/Layout';
export default {
@@ -31,7 +32,7 @@ export default {
if (!data || !data.news) throw new Error('Failed to load the news feed.');
return {
title: 'React Starter Kit',
- component: <Home news={data.news} />,
+ component: <Layout><Home news={data.news} /></Layout>,
};
},
diff --git a/src/routes/index.js b/src/routes/index.js
@@ -22,7 +22,7 @@ export default {
require('./register').default,
require('./admin').default,
- // place new routes before...
+ // Wildcard routes, e.g. { path: '*', ... } (must go last)
require('./content').default,
require('./notFound').default,
],
diff --git a/src/routes/login/Login.js b/src/routes/login/Login.js
@@ -9,7 +9,6 @@
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import Layout from '../../components/Layout';
import s from './Login.css';
class Login extends React.Component {
@@ -19,106 +18,104 @@ class Login extends React.Component {
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.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}>
+ <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">
<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>
+ <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}>
- <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>
+ <label className={s.label} htmlFor="password">
+ Password:
+ </label>
+ <input
+ className={s.input}
+ id="password"
+ type="password"
+ name="password"
+ />
</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>
+ <button className={s.button} type="submit">
+ Log in
+ </button>
</div>
- <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>
+ </form>
</div>
- </Layout>
+ </div>
);
}
}
diff --git a/src/routes/login/index.js b/src/routes/login/index.js
@@ -8,6 +8,7 @@
*/
import React from 'react';
+import Layout from '../../components/Layout';
import Login from './Login';
const title = 'Log In';
@@ -19,7 +20,7 @@ export default {
action() {
return {
title,
- component: <Login title={title} />,
+ component: <Layout><Login title={title} /></Layout>,
};
},
diff --git a/src/routes/notFound/NotFound.js b/src/routes/notFound/NotFound.js
@@ -9,7 +9,6 @@
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import Layout from '../../components/Layout';
import s from './NotFound.css';
class NotFound extends React.Component {
@@ -19,14 +18,12 @@ class NotFound extends React.Component {
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 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>
- </Layout>
+ </div>
);
}
}
diff --git a/src/routes/notFound/index.js b/src/routes/notFound/index.js
@@ -8,6 +8,7 @@
*/
import React from 'react';
+import Layout from '../../components/Layout';
import NotFound from './NotFound';
const title = 'Page Not Found';
@@ -19,7 +20,7 @@ export default {
action() {
return {
title,
- component: <NotFound title={title} />,
+ component: <Layout><NotFound title={title} /></Layout>,
status: 404,
};
},
diff --git a/src/routes/register/Register.js b/src/routes/register/Register.js
@@ -9,7 +9,6 @@
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import Layout from '../../components/Layout';
import s from './Register.css';
class Register extends React.Component {
@@ -19,14 +18,12 @@ class Register extends React.Component {
render() {
return (
- <Layout>
- <div className={s.root}>
- <div className={s.container}>
- <h1>{this.props.title}</h1>
- <p>...</p>
- </div>
+ <div className={s.root}>
+ <div className={s.container}>
+ <h1>{this.props.title}</h1>
+ <p>...</p>
</div>
- </Layout>
+ </div>
);
}
}
diff --git a/src/routes/register/index.js b/src/routes/register/index.js
@@ -8,6 +8,7 @@
*/
import React from 'react';
+import Layout from '../../components/Layout';
import Register from './Register';
const title = 'New User Registration';
@@ -19,7 +20,7 @@ export default {
action() {
return {
title,
- component: <Register title={title} />,
+ component: <Layout><Register title={title} /></Layout>,
};
},