react-native-router-flux
Version:
React Native Router using Flux architecture
971 lines (889 loc) • 28.9 kB
JavaScript
import React from 'react';
import { Image, Animated, Easing } from 'react-native';
import { createAppContainer, NavigationActions, StackActions } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import { createDrawerNavigator, DrawerActions } from 'react-navigation-drawer';
import { createMaterialTopTabNavigator, createBottomTabNavigator } from 'react-navigation-tabs';
import PropTypes from 'prop-types';
import createReducer from './Reducer';
import * as ActionConst from './ActionConst';
import { OnEnter, OnExit, assert } from './Util';
import { LeftButton, RightButton, BackButton } from './NavBar';
import LightboxRenderer from './LightboxRenderer';
import _drawerImage from '../images/menu_burger.png';
import { getActiveState, getParent, getRouteNameByKey } from './State';
import Modal from './Modal';
import Lightbox from './Lightbox';
import Drawer from './Drawer';
import Tabs from './Tabs';
import Overlay from './Overlay';
import OverlayRenderer from './OverlayRenderer';
import createStackNavigatorHOC from './createStackNavigatorHOC';
import createTabNavigatorHOC from './createTabNavigatorHOC';
let RightNavBarButton;
let LeftNavBarButton;
let BackNavBarButton;
let counter = 0;
export const actionMap = {
[ActionConst.JUMP]: 'jump',
[ActionConst.PUSH]: 'push',
[ActionConst.REPLACE]: 'replace',
[ActionConst.BACK]: 'pop',
[ActionConst.BACK_ACTION]: 'pop',
[ActionConst.POP_TO]: 'popTo',
[ActionConst.REFRESH]: 'refresh',
[ActionConst.RESET]: 'reset',
[ActionConst.PUSH_OR_POP]: 'push',
};
const reservedKeys = [
'addRef',
'back',
'children',
'create',
'dispatch',
'drawerClose',
'drawerOpen',
'execute',
'left',
'leftButton',
'navBar',
'navigate',
'on',
'onEnter',
'onExit',
'onLeft',
'onRight',
'pop',
'popTo',
'push',
'refresh',
'refs',
'removeRef',
'renderLeftButton',
'renderRightButton',
'renderTitle',
'replace',
'right',
'rightButton',
'run',
'setParams',
'title',
];
const dontInheritKeys = [
'backToInitial',
'children',
'component',
'contentComponent',
'drawer',
'hideNavBar',
'hideTabBar',
'key',
'lightbox',
'modal',
'navigator',
'navTransparent',
'overlay',
'ref',
'style',
'tabBarComponent',
'tabs',
'title',
'type',
];
function getValue(value, params) {
return value instanceof Function ? value(params) : value;
}
function getProperties(component = {}) {
const res = {};
for (const key of reservedKeys) {
if (component[key]) {
res[key] = component[key];
}
}
delete res.children;
return res;
}
function createTabBarOptions({
tabBarStyle, activeTintColor, inactiveTintColor, activeBackgroundColor, inactiveBackgroundColor, showLabel, labelStyle, tabStyle, ...props
}) {
return {
...props,
style: tabBarStyle,
activeTintColor,
inactiveTintColor,
activeBackgroundColor,
inactiveBackgroundColor,
showLabel,
labelStyle,
tabStyle,
};
}
function createNavigationOptions(params) {
const {
type,
cardStyle,
back,
backButtonImage,
backButtonTextStyle,
backTitle,
backTitleEnabled,
backToInitial,
component,
drawerIcon,
drawerImage,
drawerPosition,
getTitle,
headerLayoutPreset,
headerStyle,
headerTitleStyle,
hideDrawerButton,
hideNavBar,
hideTabBar,
icon,
init,
left,
leftButton,
leftButtonImage,
leftButtonTextStyle,
leftTitle,
navBar,
navBarButtonColor,
navigationBarStyle,
navigationBarTitleImage,
navigationBarTitleImageStyle,
navTransparent,
onLeft,
onRight,
panHandlers,
renderBackButton,
renderNavigationBar,
renderTitle,
right,
rightButton,
rightButtonImage,
rightButtonTextStyle,
rightTitle,
tabBarIcon,
tabBarLabel,
title,
titleStyle,
...props
} = params;
const NavBar = renderNavigationBar || navBar;
if (component && component.navigationOptions) {
return component.navigationOptions;
}
return ({ navigation, screenProps }) => {
const navigationParams = navigation.state.params || {};
const state = {
navigation,
...params,
...navigationParams,
...screenProps,
};
const res = {
animationEnabled: !(type === ActionConst.REPLACE || type === 'replace' || type === ActionConst.RESET || type === 'reset'),
...props,
cardStyle: navigationParams.cardStyle || cardStyle,
headerBackImage: navigationParams.backButtonImage || backButtonImage,
headerBackTitle: getValue(navigationParams.backTitle || backTitle, state),
headerBackTitleEnabled: navigationParams.backTitleEnabled || backTitleEnabled,
headerLayoutPreset: navigationParams.headerLayoutPreset || headerLayoutPreset,
headerLeft: () => getValue(navigationParams.left || left || leftButton || params.renderLeftButton, state),
headerRight: () => getValue(navigationParams.right || right || rightButton || params.renderRightButton, state),
headerStyle: getValue(navigationParams.headerStyle || headerStyle || navigationBarStyle, state),
headerTintColor: navBarButtonColor || props.tintColor || navigationParams.tintColor || navigationParams.headerTintColor,
headerTitle: getValue(navigationParams.renderTitle || renderTitle || params.renderTitle, state),
headerTitleStyle: headerTitleStyle || titleStyle,
title: getValue(navigationParams.title || title || getTitle, state),
};
const NavBarFromParams = navigationParams.renderNavigationBar || navigationParams.navBar;
if (NavBarFromParams != null) {
if (NavBarFromParams) {
res.header = data => <NavBarFromParams navigation={navigation} {...state} {...data} />;
}
} else if (NavBar) {
res.header = data => <NavBar navigation={navigation} {...state} {...data} />;
}
if (typeof navigationParams.panHandlers !== 'undefined') {
if (navigationParams.panHandlers === null) {
res.gesturesEnabled = false;
}
} else if (panHandlers === null) {
res.gesturesEnabled = false;
}
if (navigationBarTitleImage) {
res.headerTitle = <Image source={navigationBarTitleImage} style={navigationBarTitleImageStyle} />;
}
if (tabBarLabel) {
res.tabBarLabel = tabBarLabel;
}
if (tabBarIcon || icon) {
const Icon = tabBarIcon || icon;
res.tabBarIcon = data => <Icon {...state} {...data} />;
}
const componentData = {};
// copy all component static functions
if (component) {
for (const key of [
'onRight',
'onLeft',
'rightButton',
'leftButton',
'leftTitle',
'rightTitle',
'rightButtonImage',
'leftButtonImage',
'rightButtonTextStyle',
'leftButtonTextStyle',
'rightButtonIconStyle',
'leftButtonIconStyle',
'leftButtonTintColor',
'rightButtonTintColor',
]) {
if (component[key]) {
componentData[key] = component[key];
}
}
}
if (
rightButtonImage
|| rightTitle
|| params.renderRightButton
|| onRight
|| navigationParams.onRight
|| navigationParams.rightTitle
|| navigationParams.rightButtonImage
|| rightButtonTextStyle
|| ((drawerImage || drawerIcon) && !hideDrawerButton && drawerPosition === 'right')
) {
res.headerRight = () => getValue(navigationParams.right || navigationParams.rightButton || params.renderRightButton, { ...navigationParams, ...screenProps }) || (
<RightNavBarButton navigation={navigation} {...params} {...navigationParams} {...componentData} />
);
}
if (
leftButtonImage
|| backButtonImage
|| backTitle
|| leftTitle
|| params.renderLeftButton
|| leftButtonTextStyle
|| renderBackButton
|| backButtonTextStyle
|| onLeft
|| navigationParams.leftTitle
|| navigationParams.onLeft
|| navigationParams.leftButtonImage
|| navigationParams.backButtonImage
|| navigationParams.backTitle
|| ((drawerImage || drawerIcon) && !hideDrawerButton && drawerPosition !== 'right')
) {
const leftButton = navigationParams.left || navigationParams.leftButton || params.renderLeftButton;
res.headerLeft = () => getValue(leftButton, { ...params, ...navigationParams, ...screenProps })
|| (((onLeft && (leftTitle || navigationParams.leftTitle || leftButtonImage || navigationParams.leftButtonImage)) || drawerImage || drawerIcon) && (
<LeftNavBarButton navigation={navigation} {...params} {...navigationParams} {...componentData} />
))
|| res.headerLeft
|| (init ? null : (!leftButton && renderBackButton && renderBackButton(state)) || (!leftButton && <BackNavBarButton navigation={navigation} {...state} />))
|| null;
}
if (back) {
res.headerLeft = (renderBackButton && renderBackButton(state)) || (() => <BackNavBarButton navigation={navigation} {...state} />);
}
if (typeof navigationParams.left !== 'undefined' || typeof navigationParams.leftButton !== 'undefined' || typeof navigationParams.renderLeftButton !== 'undefined') {
if (navigationParams.left === null || navigationParams.leftButton === null || navigationParams.renderLeftButton === null) {
res.headerLeft = null;
}
}
// currect dynamic navigation params has priority over static scene params
// but taking them into account only if they are explicitly set (not null or undefined)
const routeParams = navigation.state.routes && navigation.state.routes[navigation.state.index].params;
if (navigationParams.hideTabBar != null) {
if (navigationParams.hideTabBar) {
res.tabBarVisible = false;
}
} else if (hideTabBar) {
res.tabBarVisible = false;
} else if (routeParams && routeParams.hideTabBar) {
res.tabBarVisible = false;
}
if (navigationParams.hideNavBar != null) {
if (navigationParams.hideNavBar) {
res.header = null;
}
} else if (hideNavBar) {
res.header = null;
}
if (navTransparent) {
res.headerTransparent = true;
res.headerStyle = {};
}
if (backToInitial) {
const userDefinedTabBarOnPress = res.tabBarOnPress;
res.tabBarOnPress = (data) => {
if (userDefinedTabBarOnPress) {
console.warn('backToInitial and tabBarOnPress were both defined and might cause unexpected navigation behaviors. I hope you know what you are doing ;-)');
userDefinedTabBarOnPress(data);
}
if (data.navigation && data.navigation.state.index !== 0) {
data.navigation.dispatch(StackActions.popToTop());
} else {
data.defaultHandler();
}
};
}
return res;
};
}
function originalRouteName(routeName) {
if (routeName.startsWith('_')) {
return routeName.substring(1);
}
return routeName;
}
function isStatelessComponent(Component) {
return !Component.prototype || typeof Component.prototype.render !== 'function';
}
function extendProps(props, store: NavigationStore) {
if (!props) {
return {};
}
const res = { ...props };
for (const transition of Object.keys(props)) {
if (
reservedKeys.indexOf(transition) === -1
&& transition.startsWith('on')
&& transition.charAt(2) >= 'A'
&& transition.charAt(2) <= 'Z'
&& typeof props[transition] === 'string'
) {
if (store[props[transition]]) {
res[transition] = params => store[props[transition]](params);
}
}
}
return res;
}
// eslint no-param-reassign: "error"
function createWrapper(Component, wrapBy, store: NavigationStore) {
if (!Component) {
return null;
}
const wrapper = wrapBy || (props => props);
// detect if the component is not functional stateless
// not sure if Component can be string-defined ("div") here
// may be there is a better way to detect stateless function component, but this should work
if (!isStatelessComponent(Component)) {
class Wrapped extends React.Component {
static propTypes = {
navigation: PropTypes.shape().isRequired,
};
constructor() {
super();
this.onRef = this.onRef.bind(this);
}
componentDidMount() {
const { navigation } = this.props;
if (this.ref && navigation && navigation.state && navigation.state.routeName) {
store.addRef(originalRouteName(navigation.state.routeName), this.ref);
}
if (this.ref && this.ref.onEnter) {
this.ref.onEnter(navigation && navigation.state);
}
}
componentWillUnmount() {
const { navigation } = this.props;
if (this.ref && navigation && navigation.state && navigation.state.routeName) {
store.deleteRef(originalRouteName(navigation.state.routeName));
}
if (this.ref && this.ref.onExit) {
this.ref.onExit(navigation && navigation.state);
}
this.ref = null;
}
onRef(ref) {
this.ref = ref;
}
render() {
const { navigation } = this.props;
if (!navigation || !navigation.state) {
return <Component ref={this.onRef} {...this.props} />;
}
return <Component ref={this.onRef} {...this.props} {...extendProps(navigation.state.params, store)} name={navigation.state.routeName} />;
}
}
return wrapper(Wrapped);
}
// if component is statless function, ref is not supported
function StatelessWrapped({ navigation, ...props }) {
return <Component {...props} navigation={navigation} {...extendProps(navigation.state.params, store)} name={navigation.state.routeName} />;
}
StatelessWrapped.propTypes = {
navigation: PropTypes.shape().isRequired,
};
return wrapper(StatelessWrapped);
}
function filterParam(data = {}) {
if (data.toString() !== '[object Object]') {
return { data };
}
const proto = (data || {}).constructor.name;
// avoid passing React Native parameters
if (!data || proto !== 'Object') {
return {};
}
return data;
}
function uniteParams(routeName, params) {
let res = {};
for (const param of params) {
if (param) {
res = { ...res, ...filterParam(param) };
}
}
res.routeName = routeName;
return res;
}
const defaultSuccess = () => {};
const defaultFailure = () => {};
export default class NavigationStore {
getStateForAction = null;
reducer = null;
_navigator = null;
externalDispatch = null;
externalState = null;
prevState = null;
externalAction = {};
refs = {};
states = {};
isLogical = {};
currentScene;
prevScene;
currentParams;
onStateChange;
set externalState(state) {
if (state && this.externalDispatch) {
this.onNavigationStateChange(this.state, state, this.externalAction);
this.state = state;
}
}
setCustomReducer = (Navigator) => {
this.getStateForAction = Navigator.router.getStateForAction;
const reducer = createReducer(this);
Navigator.router.getStateForAction = (cmd, state) => (this.reducer ? this.reducer(state, cmd) : reducer(state, cmd));
};
onEnterHandler = async (currentScene) => {
if (this.states[currentScene]) {
const handler = this[currentScene + OnEnter];
const success = this.states[currentScene].success || defaultSuccess;
const failure = this.states[currentScene].failure || defaultFailure;
if (handler) {
try {
const res = await handler(this.currentParams, this.state);
if (res) {
success(res);
} else {
failure();
}
} catch (e) {
failure({ error: e.message });
}
}
}
};
onExitHandler = (prevScene) => {
if (prevScene) {
const exitHandler = this[prevScene + OnExit];
if (exitHandler) {
try {
const res = exitHandler(this.state);
if (res instanceof Promise) {
res.then(defaultSuccess, defaultFailure);
}
} catch (e) {
console.error('Error during onExit handler:', e);
}
}
}
};
onNavigationStateChange = async (prevState, currentState, action) => {
this.state = currentState;
this.prevState = prevState;
const activeState = getActiveState(this.state);
const currentScene = activeState.routeName;
this.currentParams = { ...activeState.params, ...action.params };
this.currentScene = currentScene;
this.prevScene = this.prevState ? getActiveState(this.prevState).routeName : null;
if (this.currentScene !== this.prevScene) {
// run onExit for old scene
this.onExitHandler(this.prevScene);
setTimeout(() => this.dispatch({
type: ActionConst.FOCUS,
routeName: this.currentScene,
params: this.currentParams,
}));
this.onEnterHandler(currentScene);
} else {
const routeName = getRouteNameByKey(this.state, action.key);
if (action.type === 'Navigation/DRAWER_OPENED') {
this.onEnterHandler(routeName);
} else if (action.type === 'Navigation/DRAWER_CLOSED') {
this.onExitHandler(routeName);
}
}
if (this.onStateChange) {
this.onStateChange(prevState, currentState, action);
}
};
setTopLevelNavigator = (navigatorRef) => {
this._navigator = navigatorRef;
};
addRef = (name, ref) => {
this.refs[name] = ref;
};
deleteRef = (name) => {
delete this.refs[name];
};
create = (scene: Scene, params = {}, wrapBy = props => props) => {
assert(!Array.isArray(scene), 'Router should contain only one scene, please wrap your scenes with root Scene ');
RightNavBarButton = wrapBy(RightButton);
LeftNavBarButton = wrapBy(LeftButton);
BackNavBarButton = wrapBy(BackButton);
const Navigator = this.processScene(scene, params, [], wrapBy);
// set initial state
this.onNavigationStateChange(null, Navigator.router.getStateForAction(NavigationActions.init()), NavigationActions.init());
this.setCustomReducer(Navigator);
return createAppContainer(Navigator);
};
createAction = name => (args) => {
// console.log(`Transition to state=${name}`);
if (this.isLogical[name]) {
this[name](args);
} else {
setTimeout(() => this[name](args));
}
};
processScene = (scene: Scene, inheritProps = {}, clones = [], wrapBy) => {
assert(scene.props, 'props should be defined');
if (!scene.props.children) {
return null;
}
const res = {};
const order = [];
const {
navigator, renderer, contentComponent, drawerWidth, drawerLockMode, tabBarPosition, lazy, duration, ...parentProps
} = scene.props;
let {
tabs, modal, lightbox, overlay, drawer, transitionConfig, tabBarComponent,
} = parentProps;
if (scene.type === Modal) {
modal = true;
} else if (scene.type === Drawer) {
drawer = true;
} else if (scene.type === Lightbox) {
lightbox = true;
} else if (scene.type === Tabs) {
tabs = true;
} else if (scene.type === Overlay) {
overlay = true;
}
if (duration !== undefined && !transitionConfig) {
transitionConfig = () => ({
transitionSpec: {
duration,
timing: Animated.timing,
easing: Easing.step0,
},
});
}
const commonProps = { ...inheritProps, ...parentProps };
delete commonProps.children;
delete commonProps.component;
// add inherit props
for (const pkey of Object.keys(commonProps)) {
if (dontInheritKeys.includes(pkey) && (pkey === 'type' || pkey === 'hideNavBar' || !parentProps[pkey])) {
delete commonProps[pkey];
}
}
if (drawer) {
commonProps.drawerImage = commonProps.drawerImage || _drawerImage;
}
const children = !Array.isArray(parentProps.children) ? [parentProps.children] : [].concat(...parentProps.children);
// add clone scenes
if (!drawer && !tabs && !overlay) {
children.push(...clones);
}
// add all clones
for (const child of children) {
if (child && child.props.clone) {
if (clones.indexOf(child) === -1) {
clones.push(child);
}
}
}
let initialRouteName;
let initialRouteParams;
for (const child of children) {
// allow null/false child, useful for conditionals
if (!child) {
continue;
}
const key = child.key || `key${(counter += 1)}`;
const init = key === children[0].key;
assert(reservedKeys.indexOf(key) === -1, `Scene name cannot be reserved word: ${child.key}`);
const {
component, type = tabs || drawer ? 'jump' : 'push', path, onEnter, onExit, on, failure, success, wrap, initial = false, ...props
} = child.props;
if (!this.states[key]) {
this.states[key] = {};
}
for (const transition of Object.keys(props)) {
if (reservedKeys.indexOf(transition) === -1 && props[transition] instanceof Function) {
this.states[key][transition] = props[transition];
}
}
delete props.children;
if (success) {
this.states[key].success = success instanceof Function ? success : this.createAction(success);
}
if (failure) {
this.states[key].failure = failure instanceof Function ? failure : this.createAction(failure);
}
if (path) {
this.states[key].path = path;
}
// console.log(`KEY ${key} LEGACY {legacy} PATH ${path} DRAWER ${drawer} TABS ${tabs} WRAP ${wrap}`, JSON.stringify(commonProps));
const screen = {
screen: createWrapper(component, wrapBy, this) || this.processScene(child, commonProps, clones) || (lightbox && (() => null)),
navigationOptions: createNavigationOptions({
...commonProps,
hideNavBar: parentProps.hideNavBar,
...getProperties(component),
...child.props,
init,
component,
}),
};
// wrap component inside own navbar for tabs/drawer parent controllers
// don't wrap child scenes for custom navigators/renderers
let wrapNavBar = drawer || (tabs && !navigator && !renderer) || wrap;
if (wrap === false || commonProps.wrap === false) {
wrapNavBar = false;
}
if (component && wrapNavBar) {
res[key] = {
screen: this.processScene(
{
key,
props: {
children: {
key: `_${key}`,
props: { ...child.props, wrap: false },
},
},
},
commonProps,
clones,
wrapBy,
),
navigationOptions: createNavigationOptions({
...commonProps,
...child.props,
hideNavBar: true,
}),
};
} else {
res[key] = screen;
}
// a bit of magic, create all 'actions'-shortcuts inside navigationStore
props.init = true;
if (!this[key]) {
this.isLogical[key] = !!component;
this[key] = new Function(
'actions',
'props',
'type',
`return function ${
key.replace(/\W/g, '_') // eslint-disable-line no-new-func
}(params){ actions.execute(type, '${key}', props, params)}`,
)(this, { error: '', ...commonProps, ...props }, type);
}
if ((onEnter || on || (component && component.onEnter)) && !this[key + OnEnter]) {
this[key + OnEnter] = onEnter || on || component.onEnter;
}
if ((onExit || (component && component.onExit)) && !this[key + OnExit]) {
this[key + OnExit] = onExit || component.onExit;
}
order.push(key);
if (initial || child.props.initial || !initialRouteName) {
initialRouteName = key;
initialRouteParams = { ...commonProps, ...props };
}
}
const mode = modal ? 'modal' : 'card';
const navigationConfig = {
lazy,
initialRouteName,
initialRouteParams,
contentComponent,
order,
...commonProps,
navigationOptions: createNavigationOptions(commonProps),
};
if (navigator) {
return navigator(res, navigationConfig);
}
if (renderer) {
return tabs ? createTabNavigatorHOC(renderer)(res, navigationConfig) : createStackNavigatorHOC(renderer)(res, navigationConfig);
}
if (lightbox) {
return createStackNavigatorHOC(LightboxRenderer)(res, {
mode,
initialRouteParams,
initialRouteName,
...commonProps,
navigationOptions: createNavigationOptions(commonProps),
});
}
if (tabs) {
let createTabNavigator = createMaterialTopTabNavigator;
if (tabBarPosition !== 'top') {
createTabNavigator = createBottomTabNavigator;
}
return createTabNavigator(res, {
lazy,
tabBarComponent,
initialRouteName,
initialRouteParams,
tabBarPosition,
order,
...commonProps,
tabBarOptions: createTabBarOptions(commonProps),
navigationOptions: createNavigationOptions(commonProps),
});
}
if (drawer) {
const config = {
initialRouteName,
contentComponent,
order,
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle',
...commonProps,
};
if (drawerWidth) {
config.drawerWidth = drawerWidth;
}
if (drawerLockMode) {
config.drawerLockMode = drawerLockMode;
}
return createDrawerNavigator(res, config);
}
if (overlay) {
return createTabNavigatorHOC(OverlayRenderer)(res, {
lazy,
initialRouteName,
contentComponent,
initialRouteParams,
order,
...commonProps,
tabBarOptions: createTabBarOptions(commonProps),
navigationOptions: createNavigationOptions(commonProps),
});
}
return createStackNavigator(res, {
mode,
initialRouteParams,
initialRouteName,
...commonProps,
transitionConfig,
navigationOptions: createNavigationOptions(commonProps),
});
};
dispatch = (action) => {
if (this.externalDispatch) {
this.externalAction = action;
this.externalDispatch(action);
} else if (this._navigator) {
this._navigator.dispatch(action);
}
};
execute = (actionType, routeName, ...params) => {
const res = uniteParams(routeName, params);
const overridenType = res.type || actionType;
const type = actionMap[overridenType] || overridenType;
if (type === 'pop') {
this[type](res);
} else {
this[type](routeName, res);
}
};
push = (routeName, data) => {
const params = filterParam(data);
this.dispatch({ type: StackActions.PUSH, routeName, params });
};
jump = (routeName, data) => {
const params = filterParam(data);
this.dispatch({ type: NavigationActions.NAVIGATE, routeName, params });
};
drawerOpen = () => {
this.dispatch(DrawerActions.openDrawer());
};
drawerClose = () => {
this.dispatch(DrawerActions.closeDrawer());
};
drawerToggle = () => {
this.dispatch(DrawerActions.toggleDrawer());
};
refresh = (data, sceneKey = null) => {
const params = filterParam(data);
const { key } = getActiveState(this.state);
this.dispatch(
NavigationActions.setParams({
key: sceneKey || key,
params,
}),
);
};
pop = ({ timeout, key, ...params } = {}) => {
const res = filterParam(params);
if (timeout) {
setTimeout(() => this.pop(params), timeout);
} else {
this.dispatch(NavigationActions.back({ key }));
if (res.refresh) {
this.refresh(res.refresh);
}
}
return true;
};
popTo = (routeName, data) => {
const params = filterParam(data);
this.dispatch({ type: ActionConst.POP_TO, routeName, params });
};
popAndPush = (routeName, data) => {
const params = filterParam(data);
this.dispatch({ type: ActionConst.POP_AND_PUSH, routeName, params });
};
replace = (routeName, data) => {
const params = filterParam(data);
this.dispatch({ type: ActionConst.REPLACE, routeName, params });
};
reset = (routeName, data) => {
const params = filterParam(data);
const parent = getParent(this.state, routeName);
this.dispatch(
StackActions.reset({
index: 0,
key: parent ? parent.key : null,
actions: [
NavigationActions.navigate({
routeName,
params,
}),
],
}),
);
};
}