commit 89ceb6ab614ec4258f47b97e3d412350baab1815
parent 8b655d36070ef6a9ba1c69327204ca78b584312b
Author: Vladimir Kutepov <frenzzy.man@gmail.com>
Date: Tue, 4 Oct 2016 23:25:49 +0400
Add the Admin page with redirect example (#879)
- Add support of redirects at the routing level, e.g. { path: '/admin', action() { return { redirect: '/login' } } }
- Remove context.history as it's no longer used on the server in favor of core/history.js singleton
- Create /admin page example with a redirect to /login page
Diffstat:
10 files changed, 117 insertions(+), 29 deletions(-)
diff --git a/src/client.js b/src/client.js
@@ -13,16 +13,13 @@ import ReactDOM from 'react-dom';
import FastClick from 'fastclick';
import UniversalRouter from 'universal-router';
import queryString from 'query-string';
-import createBrowserHistory from 'history/createBrowserHistory';
import { createPath } from 'history/PathUtils';
+import history from './core/history';
import App from './components/App';
// Global (context) variables that can be easily accessed from any React component
// https://facebook.github.io/react/docs/context.html
const context = {
- // Navigation manager, e.g. history.push('/home')
- // https://github.com/mjackson/history
- history: createBrowserHistory(),
// Enables critical path CSS rendering
// https://github.com/kriasoft/isomorphic-style-loader
insertCss: (...styles) => {
@@ -111,7 +108,7 @@ let onRenderComplete = function initialRenderComplete() {
FastClick.attach(document.body);
const container = document.getElementById('app');
-let currentLocation = context.history.location;
+let currentLocation = history.location;
let routes = require('./routes').default;
// Re-render the app when window.location changes
@@ -122,7 +119,7 @@ async function onLocationChange(location) {
scrollY: window.pageYOffset,
};
// Delete stored scroll position for next page if any
- if (context.history.action === 'PUSH') {
+ if (history.action === 'PUSH') {
delete scrollPositionsHistory[location.key];
}
currentLocation = location;
@@ -136,15 +133,21 @@ async function onLocationChange(location) {
query: queryString.parse(location.search),
});
- // Render the result of the resolved route into the DOM
- // if the location was not changed during the routing process
- if (currentLocation.key === location.key) {
- ReactDOM.render(
- <App context={context}>{route.component}</App>,
- container,
- () => onRenderComplete(route, location)
- );
+ // Prevent multiple page renders during the routing process
+ if (currentLocation.key !== location.key) {
+ return;
}
+
+ if (route.redirect) {
+ history.replace(route.redirect);
+ return;
+ }
+
+ ReactDOM.render(
+ <App context={context}>{route.component}</App>,
+ container,
+ () => onRenderComplete(route, location)
+ );
} catch (err) {
if (process.env.NODE_ENV !== 'production') {
throw err;
@@ -158,7 +161,7 @@ async function onLocationChange(location) {
// Handle client-side navigation by using HTML5 History API
// For more information visit https://github.com/mjackson/history#readme
-context.history.listen(onLocationChange);
+history.listen(onLocationChange);
onLocationChange(currentLocation);
// Enable Hot Module Replacement (HMR)
diff --git a/src/components/App.js b/src/components/App.js
@@ -10,9 +10,6 @@
import React, { PropTypes } from 'react';
const ContextType = {
- // Navigation manager, e.g. history.push('/home')
- // https://github.com/mjackson/history
- history: PropTypes.object.isRequired,
// Enables critical path CSS rendering
// https://github.com/kriasoft/isomorphic-style-loader
insertCss: PropTypes.func.isRequired,
diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js
@@ -20,6 +20,8 @@ function Footer() {
<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>
diff --git a/src/components/Link/Link.js b/src/components/Link/Link.js
@@ -8,6 +8,7 @@
*/
import React, { Component, PropTypes } from 'react';
+import history from '../../core/history';
function isLeftClickEvent(event) {
return event.button === 0;
@@ -25,10 +26,6 @@ class Link extends Component {
onClick: PropTypes.func,
};
- static contextTypes = {
- history: PropTypes.object.isRequired,
- };
-
handleClick = (event) => {
if (this.props.onClick) {
this.props.onClick(event);
@@ -43,7 +40,7 @@ class Link extends Component {
}
event.preventDefault();
- this.context.history.push(this.props.to);
+ history.push(this.props.to);
};
render() {
diff --git a/src/core/history.js b/src/core/history.js
@@ -0,0 +1,5 @@
+import createBrowserHistory from 'history/createBrowserHistory';
+
+// Navigation manager, e.g. history.push('/home')
+// https://github.com/mjackson/history
+export default process.env.BROWSER && createBrowserHistory();
diff --git a/src/routes/admin/Admin.css b/src/routes/admin/Admin.css
@@ -0,0 +1,21 @@
+/**
+ * React Starter Kit (https://www.reactstarterkit.com/)
+ *
+ * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE.txt file in the root directory of this source tree.
+ */
+
+@import '../../components/variables.css';
+
+.root {
+ padding-left: 20px;
+ padding-right: 20px;
+}
+
+.container {
+ margin: 0 auto;
+ padding: 0 0 40px;
+ max-width: var(--max-content-width);
+}
diff --git a/src/routes/admin/Admin.js b/src/routes/admin/Admin.js
@@ -0,0 +1,32 @@
+/**
+ * React Starter Kit (https://www.reactstarterkit.com/)
+ *
+ * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE.txt file in the root directory of this source tree.
+ */
+
+import React, { PropTypes } from 'react';
+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>
+ </div>
+ </div>
+ </Layout>
+ );
+}
+
+Admin.propTypes = {
+ title: PropTypes.string.isRequired,
+};
+
+export default withStyles(s)(Admin);
diff --git a/src/routes/admin/index.js b/src/routes/admin/index.js
@@ -0,0 +1,31 @@
+/**
+ * React Starter Kit (https://www.reactstarterkit.com/)
+ *
+ * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE.txt file in the root directory of this source tree.
+ */
+
+import React from 'react';
+import Admin from './Admin';
+
+const title = 'Admin Page';
+const isAdmin = false;
+
+export default {
+
+ path: '/admin',
+
+ action() {
+ if (!isAdmin) {
+ return { redirect: '/login' };
+ }
+
+ return {
+ title,
+ component: <Admin title={title} />,
+ };
+ },
+
+};
diff --git a/src/routes/index.js b/src/routes/index.js
@@ -20,6 +20,7 @@ export default {
require('./contact').default,
require('./login').default,
require('./register').default,
+ require('./admin').default,
// place new routes before...
require('./content').default,
diff --git a/src/server.js b/src/server.js
@@ -18,7 +18,6 @@ import jwt from 'jsonwebtoken';
import React from 'react';
import ReactDOM from 'react-dom/server';
import UniversalRouter from 'universal-router';
-import createMemoryHistory from 'history/createMemoryHistory';
import PrettyError from 'pretty-error';
import App from './components/App';
import Html from './components/Html';
@@ -91,11 +90,6 @@ app.get('*', async (req, res, next) => {
// Global (context) variables that can be easily accessed from any React component
// https://facebook.github.io/react/docs/context.html
const context = {
- // Navigation manager, e.g. history.push('/home')
- // https://github.com/mjackson/history
- history: createMemoryHistory({
- initialEntries: [req.url],
- }),
// Enables critical path CSS rendering
// https://github.com/kriasoft/isomorphic-style-loader
insertCss: (...styles) => {
@@ -109,6 +103,11 @@ app.get('*', async (req, res, next) => {
query: req.query,
});
+ if (route.redirect) {
+ res.redirect(route.status || 302, route.redirect);
+ return;
+ }
+
const data = { ...route };
data.children = ReactDOM.renderToString(<App context={context}>{route.component}</App>);
data.style = [...css].join('');