Upgrade to React Router v4 (#667)

* Upgrade to React Router v4

* Fix pages not change when the URL was changed.

This issue is due to the Redux `connect` wrapper around `<App/>`.
`connect` diffs changes in regular props to know when to update the
component, but doesn't check context props like `location`.
See
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/blocked-updates.md.

* Update to new `history` methods.
This commit is contained in:
Caleb
2017-10-12 19:10:43 -06:00
committed by Shawn Erquhart
parent b0bf60bd7d
commit dbe96d33f9
11 changed files with 92 additions and 109 deletions

View File

@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import React from "react";
import ImmutablePropTypes from "react-immutable-proptypes";
import { IndexLink } from "react-router";
import { Link } from 'react-router-dom';
import { IconMenu, Menu, MenuItem } from "react-toolbox/lib/menu";
import Avatar from "react-toolbox/lib/avatar";
import AppBar from "react-toolbox/lib/app_bar";
@ -66,9 +66,9 @@ export default class AppHeader extends React.Component {
onLeftIconClick={toggleDrawer}
onRightIconClick={this.handleRightIconClick}
>
<IndexLink to="/" className={styles.homeLink}>
<Link to="/" className={styles.homeLink}>
<FontIcon value="home" className={styles.icon} />
</IndexLink>
</Link>
<IconMenu
theme={styles}
icon="add"

View File

@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import { DragSource, DropTarget, HTML5DragDrop } from 'react-simple-dnd';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
import moment from 'moment';
import { capitalize } from 'lodash'
import { Card, CardTitle, CardText, CardActions } from 'react-toolbox/lib/card';

View File

@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { IndexLink } from "react-router";
import { Route, Switch, Link } from 'react-router-dom';
import FontIcon from 'react-toolbox/lib/font_icon';
import { Navigation } from 'react-toolbox/lib/navigation';
import { Notifs } from 'redux-notifications';
@ -21,6 +21,11 @@ import AppHeader from '../components/AppHeader/AppHeader';
import { Loader, Toast } from '../components/UI/index';
import { getCollectionUrl, getNewEntryUrl } from '../lib/urlHelper';
import { SIMPLE, EDITORIAL_WORKFLOW } from '../constants/publishModes';
import DashboardPage from './DashboardPage';
import CollectionPage from './CollectionPage';
import EntryPage from './EntryPage';
import SearchPage from './SearchPage';
import NotFoundPage from './NotFoundPage';
import styles from './App.css';
import sidebarStyles from './Sidebar.css';
@ -38,7 +43,6 @@ class App extends React.Component {
static propTypes = {
auth: ImmutablePropTypes.map,
children: PropTypes.node,
config: ImmutablePropTypes.map,
collections: ImmutablePropTypes.orderedMap,
createNewEntryInCollection: PropTypes.func.isRequired,
@ -104,7 +108,6 @@ class App extends React.Component {
const {
user,
config,
children,
collections,
toggleSidebar,
runCommand,
@ -140,7 +143,7 @@ class App extends React.Component {
<section>
<h1 className={sidebarStyles.heading}>Publishing</h1>
<div className={sidebarStyles.linkWrapper}>
<IndexLink to="/" className={sidebarStyles.viewEntriesLink}>Editorial Workflow</IndexLink>
<Link to="/" className={sidebarStyles.viewEntriesLink}>Editorial Workflow</Link>
</div>
</section>
}
@ -193,7 +196,14 @@ class App extends React.Component {
<div className={styles.entriesPanel}>
{ isFetching && <TopBarProgress /> }
<div>
{children}
<Switch>
<Route exact path='/' component={DashboardPage} />
<Route exact path="/collections/:name" component={CollectionPage} />
<Route path="/collections/:name/entries/new" render={(props) => (<EntryPage {...props} newRecord={true}/>)} />
<Route path="/collections/:name/entries/:slug" component={EntryPage} />
<Route path="/search/:searchTerm" component={SearchPage} />
<Route component={NotFoundPage} />
</Switch>
</div>
</div>
</div>

View File

@ -68,7 +68,7 @@ class CollectionPage extends React.Component {
function mapStateToProps(state, ownProps) {
const { collections, config } = state;
const { name, slug } = ownProps.params;
const { name } = ownProps.match.params;
const publicFolder = config.get('public_folder');
const collection = name ? collections.get(name) : collections.first();
const page = state.entries.getIn(['pages', collection.get('name'), 'page']);
@ -76,7 +76,7 @@ function mapStateToProps(state, ownProps) {
const entries = selectEntries(state, collection.get('name'));
const isFetching = state.entries.getIn(['pages', collection.get('name'), 'isFetching'], false);
return { slug, publicFolder, collection, collections, page, entries, isFetching };
return { publicFolder, collection, collections, page, entries, isFetching };
}
export default connect(mapStateToProps)(CollectionPage);

View File

@ -56,11 +56,10 @@ class EntryPage extends React.Component {
loadEntry(collection, slug);
}
this.unlisten = history.listenBefore((location) => {
this.unblock = history.block((location) => {
if (this.props.entryDraft.get('hasChanged')) {
return "Are you sure you want to leave this page?";
}
return true;
});
}
@ -84,7 +83,7 @@ class EntryPage extends React.Component {
componentWillUnmount() {
this.props.discardDraft();
this.unlisten();
this.unblock();
}
createDraft = (entry) => {
@ -161,9 +160,9 @@ class EntryPage extends React.Component {
function mapStateToProps(state, ownProps) {
const { collections, entryDraft } = state;
const slug = ownProps.params.slug;
const collection = collections.get(ownProps.params.name);
const newEntry = ownProps.route && ownProps.route.newRecord === true;
const slug = ownProps.match.params.slug;
const collection = collections.get(ownProps.match.params.name);
const newEntry = ownProps.newRecord === true;
const fields = selectFields(collection, slug);
const entry = newEntry ? null : selectEntry(state, collection.get('name'), slug);
const boundGetAsset = getAsset.bind(null, state);

View File

@ -67,7 +67,7 @@ function mapStateToProps(state, ownProps) {
const entries = selectSearchedEntries(state);
const collections = state.collections.toIndexedSeq();
const publicFolder = state.config.get('public_folder');
const searchTerm = ownProps.params && ownProps.params.searchTerm;
const { searchTerm } = ownProps.match.params;
return { isFetching, page, collections, entries, publicFolder, searchTerm };
}

View File

@ -1,23 +1,21 @@
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router';
import routes from './routing/routes';
import history, { syncHistory } from './routing/history';
import { Route } from 'react-router-dom';
import { ConnectedRouter } from 'react-router-redux';
import history from './routing/history';
import configureStore from './redux/configureStore';
import { setStore } from './valueObjects/AssetProxy';
import App from './containers/App';
const store = configureStore();
// Create an enhanced history that syncs navigation events with the store
syncHistory(store);
setStore(store);
const Root = () => (
<Provider store={store}>
<Router history={history}>
{routes}
</Router>
<ConnectedRouter history={history}>
<Route component={App}/>
</ConnectedRouter>
</Provider>
);

View File

@ -1,14 +1,5 @@
import { createHashHistory } from 'history';
import { useRouterHistory } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import createHistory from 'history/createHashHistory';
let history = useRouterHistory(createHashHistory)({
queryKey: false
});
let history = createHistory();
const syncHistory = (store) => {
history = syncHistoryWithStore(history, store);
};
export { syncHistory };
export default history;

View File

@ -1,35 +0,0 @@
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from '../containers/App';
import DashboardPage from '../containers/DashboardPage';
import CollectionPage from '../containers/CollectionPage';
import EntryPage from '../containers/EntryPage';
import SearchPage from '../containers/SearchPage';
import NotFoundPage from '../containers/NotFoundPage';
export default (
<Route path="/" component={App}>
<IndexRoute component={DashboardPage} />
<Route
path="/collections/:name"
component={CollectionPage}
/>
<Route
path="/collections/:name/entries/new"
component={EntryPage}
newRecord
/>
<Route
path="/collections/:name/entries/:slug"
component={EntryPage}
/>
<Route
path="/search/:searchTerm"
component={SearchPage}
/>
<Route
path="*"
component={NotFoundPage}
/>
</Route>
);