routing and drawer performance fixes

This commit is contained in:
Bart Akeley 2017-09-10 19:20:26 -05:00
parent 8ce9d4e045
commit f3d383fb89
4 changed files with 95 additions and 74 deletions

View file

@ -7,7 +7,7 @@ import DrawerMenu from './pages/DrawerMenu';
import rxjsconfig from 'recompose/rxjsObservableConfig';
import setObservableConfig from 'recompose/setObservableConfig';
import TopToolbar from './components/TopToolbar';
import { NativeRouter, Route, Redirect } from 'react-router-native';
import { NativeRouter, Route, Redirect, AndroidBackButton } from 'react-router-native';
import List from './pages/List';
import FoodItemDetail from './pages/FoodItemDetail';
import PlaceDetail from './pages/PlaceDetail';
@ -16,53 +16,42 @@ import CreateFoodItem from './pages/CreateFoodItem';
setObservableConfig(rxjsconfig);
type State = {
drawerOpen: boolean,
};
export default class App extends Component {
state = { drawerOpen: false };
state: State;
static displayName = 'App';
_drawer: Drawer;
toggleDrawer = () => {
this.setState(({ drawerOpen }) => ({ drawerOpen: !drawerOpen }));
};
closeDrawer = () => {
this.setState({ drawerOpen: false });
this._drawer.close();
};
openDrawer = () => {
this.setState({ drawerOpen: true });
this._drawer.open();
};
render() {
const { drawerOpen } = this.state;
return (
<NativeRouter>
<ThemeProvider uiTheme={theme}>
<Drawer
ref={ref => (this._drawer = ref)}
type="overlay"
open={drawerOpen}
onOpen={this.openDrawer}
onClose={this.closeDrawer}
openDrawerOffset={100}
content={<DrawerMenu onCloseDrawer={this.closeDrawer} />}
>
<View style={theme.page.container}>
<TopToolbar toggleSideMenu={this.toggleDrawer} />
<Redirect from="/" to="/list/food" />
<Route path="/list/:type" component={List} />
<Route path="/foodItem/:id" component={FoodItemDetail} />
<Route path="/place/:id" component={PlaceDetail} />
<Route path="/createFoodItem" component={CreateFoodItem} />
</View>
</Drawer>
</ThemeProvider>
<AndroidBackButton>
<ThemeProvider uiTheme={theme}>
<Drawer
ref={ref => (this._drawer = ref)}
type="overlay"
openDrawerOffset={100}
content={<DrawerMenu onCloseDrawer={this.closeDrawer} />}
tweenDuration={150}
>
<View style={theme.page.container}>
<TopToolbar toggleSideMenu={this.openDrawer} />
<Redirect from="/" to="/list/food" />
<Route path="/list/:type" component={List} />
<Route path="/foodItem/:id" component={FoodItemDetail} />
<Route path="/place/:id" component={PlaceDetail} />
<Route path="/createFoodItem" component={CreateFoodItem} />
</View>
</Drawer>
</ThemeProvider>
</AndroidBackButton>
</NativeRouter>
);
}

View file

@ -23,7 +23,6 @@ class TopToolbar extends Component {
const { router: { route, history } } = this.context;
history.replace({
...route.location,
state: {
...route.location.state,
viewMode: action,
@ -37,28 +36,52 @@ class TopToolbar extends Component {
onSearchChanged: () => {};
getRightElement = () => {
const pathname = path(['router', 'route', 'location', 'pathname'], this.context);
const viewMode = path(['router', 'route', 'location', 'state', 'viewMode'], this.context);
switch (pathname.match(/^\/\w*/)[0]) {
case '/list': {
return viewMode === 'map' ? 'list' : 'map';
}
default: {
return '';
}
}
};
getSearchable = () => {
const pathname = path(['router', 'route', 'location', 'pathname'], this.context);
if (!/list/.test(pathname)) {
return {};
}
return {
searchable: {
autoFocus: true,
onSearchPressed: this.onSearchPressed,
onSearchClosed: this.onSearchClosed,
onChangeText: this.onSearchChanged,
},
};
};
render() {
const { toggleSideMenu } = this.props;
const history = path(['router', 'history'], this.context);
const state = path(['router', 'route', 'location', 'state'], this.context) || {};
const { routeTitle, viewMode } = state;
const routeTitle = path(['router', 'route', 'location', 'state', 'routeTitle'], this.context);
const title = routeTitle || this.props.title;
const isBasePage = history.index === 0;
const modeSwitchIcon = viewMode === 'map' ? 'list' : 'map';
return (
<Toolbar
leftElement={isBasePage ? 'menu' : 'arrow-back'}
onLeftElementPress={isBasePage ? toggleSideMenu : history.goBack}
centerElement={title}
rightElement={modeSwitchIcon}
rightElement={this.getRightElement()}
onRightElementPress={this.onRightPress}
searchable={{
autoFocus: true,
onSearchPressed: this.onSearchPressed,
onSearchClosed: this.onSearchClosed,
onChangeText: this.onSearchChanged,
}}
{...this.getSearchable()}
/>
);
}

View file

@ -8,53 +8,57 @@ import { type RoutingContextFlowTypes, routingContextPropTypes } from '../routes
const tabs = ['food', 'places'];
const getTabIndex = (tabName: string) => tabs.indexOf(tabName);
const getTabName = (index: number) => tabs[index];
const getTabIndex = ({ match: { params: { type = '' } } }: RoutingContextFlowTypes): number => {
return tabs.indexOf(type) || 0;
};
type TabView = {
goToPage: (page: number) => void,
};
type Props = {
match: {
params: {
type?: string,
},
state: {
currentPage: number,
},
};
class List extends Component {
static contextTypes = routingContextPropTypes;
props: Props;
props: RoutingContextFlowTypes;
context: RoutingContextFlowTypes;
tabView: TabView;
// TODO: find a more routing friendly tab component (or build one)
// sync route changes into tab state (yuck)
componentWillReceiveProps(newProps: Props) {
const { match: { params: { type: newType } } } = newProps;
const { match: { params: { type } } } = this.props;
if (!!this.tabView && !!newType && type !== newType) {
const page = getTabIndex(newType);
this.tabView.goToPage(page);
updateTabRoute = ({ i }: { i: number }) => {
const currentTab = getTabIndex(this.props);
if (currentTab !== i) {
/*
routing is slow in react-native so this setTimeout allows for the stateful
TabView component to re-render itself before we make a route change
(which we expect will then re-render with no tab change)
*/
setTimeout(() => {
this.context.router.history.replace(`/list/${tabs[i]}`);
});
}
};
componentWillReceiveProps(nextProps: RoutingContextFlowTypes) {
if (!this.tabView) {
return;
}
const currentTab = getTabIndex(this.props);
const nextTab = getTabIndex(nextProps);
if (currentTab !== nextTab) {
this.tabView.goToPage(nextTab);
}
}
// sync tab state into route state (yuck)
updateTabRoute = ({ i }: { i: number }) => {
const type = getTabName(i);
this.context.router.history.replace(`/list/${type}`);
};
setTabViewRef = (tabView: TabView) => (this.tabView = tabView);
render() {
const { match: { params: { type = '' } } } = this.props;
const page = getTabIndex(type);
return (
<ScrollableTabView
ref={this.setTabViewRef}
@ -64,7 +68,7 @@ class List extends Component {
tabBarInactiveTextColor={theme.topTabs.selectedTextColor}
prerenderingSiblingsNumber={Infinity}
onChangeTab={this.updateTabRoute}
initialPage={page}
initialPage={getTabIndex(this.props)}
>
<Food tabLabel="FOOD" />
<Places tabLabel="PLACES" />

View file

@ -77,4 +77,9 @@ export type RoutingContextFlowTypes = {
},
},
},
match: {
params: {
type?: string,
},
},
};