ant-design / ant-design-mobile-rn

Ant Design for React Native
https://rn.mobile.ant.design/
MIT License
3.1k stars 612 forks source link

react-navigation,路由切换后,出现Modal不显示的情况。 #539

Closed Mageenz closed 5 years ago

Mageenz commented 5 years ago

Reproduction link

https://youtu.be/hvlxGuI0pHw

Steps to reproduce

公司内部的项目,整个代码不好放出来。

ant-design/react-native的版本是3.1.8,版本选择里怎么没有3.1.8?

步骤:从设置页面退出登录,回到登录页,然后Modal就不再显示了。

看了一下午的源码,发现退出登录后,再调用Modal.alert()时,portal.host中的这个方法不会再执行了。

_mount = (children: React.ReactNode, _key?: number) => {
    const key = _key || this._nextKey  ;
    if (this._manager) {
      this._manager.mount(key, children);
    } else {
      this._queue.push({ type: 'mount', key, children });
    }

    return key;
  };

也就是说是

TopViewEventEmitter.addListener(addType, this._mount);

事件监听器失效了。

设置页面退出登录的代码如下:

Modal.alert('温馨提示', '确定退出当前账号么?', [
  {text: '取消'},
  {text: '确定', onPress: () => {
    Storage.delete('token');
    this.navigator.push('Login');
  }}
])

What is expected?

期望切换路由后能正常弹出Modal。

What is actually happening?

目前切换路由后,Modal失效了。

Environment Info
antd 3.1.17
React react-native@0.59.6
System ios12.2
Browser

只有退出登录的切换路由才会导致这个问题,其他页面的切换没发现有这个问题。

路由的定义如下,登录和设置页面是下面的Login和Setting:

import React from 'react';
import { createStackNavigator, createBottomTabNavigator, createSwitchNavigator } from 'react-navigation';
// import StackViewStyleInterpolator from 'react-navigation-stack/dist/views/StackView/StackViewStyleInterpolator';
import { DeviceEventEmitter, StatusBar } from 'react-native';
import { Login, PwdReset, PwdVerify } from '../pages/auth';
import { Home } from '../pages/home';
import { Mine, ExtendList, ExtendContent, PersonInfo, Setting, AboutMe } from '../pages/account';

import {
  MerchantList,
  MerchantAdd,
  MerchantManage,
  MerchantDetail,
  MerchantAddSuccess,
  MerchantOperatorList,
  MerchantRateUpdate,
  MerchantExceptionList,
} from '../pages/merchant';

import { QrCodeEdit, QrCodeDetail, ActivateCode, StoreList } from '../pages/payment';

import { Search } from '../pages/common';
import { Image } from '../components';
import { ScanCode } from '../pages/scan';
import { ifAndroid } from '../utils';
import { Img } from '@assets';

import {
  AdditionalInfo,
  BasicInfo,
  BankFuzzyQuery,
  BillingInfo,
  IdCardInfo,
  MaterialInfo,
  StorePhotos,
} from '../pages/sign';

import { Guide, Splash } from '../pages/guide';

const TabBarIcon = ({ focused, tabType }) => {
  const tabTypes = [
    {
      active: 'HomeTabActiveIcon',
      inactive: 'HomeTabInactiveIcon',
    },
    {
      active: 'MerchantTabActiveIcon',
      inactive: 'MerchantTabInactiveIcon',
    },
    {
      active: 'AccountTabActiveIcon',
      inactive: 'AccountTabInactiveIcon',
    },
  ];

  const crtTab = tabTypes[tabType];
  return (
    <Image
      style={{ width: 24, height: 24, marginBottom: -6 }}
      source={focused ? Img[crtTab.active] : Img[crtTab.inactive]}
    />
  );
};

const Auth = createStackNavigator(
  {
    Login,
    PwdVerify,
    PwdReset,
  },
  {
    headerMode: 'none',
    navigationOptions: () => ({
      header: null,
      gesturesEnabled: true,
    }),
    defaultNavigationOptions: {
      gesturesEnabled: false,
    },
    // transitionConfig: () => ({
    //   screenInterpolator: StackViewStyleInterpolator.forHorizontal, // StackNavigator设置android水平转场动画
    // }),
  }
);

let lastTabPosition = 0;

const Tab = createBottomTabNavigator(
  {
    Home: {
      screen: Home,
      navigationOptions: {
        tabBarLabel: '首页',
        tabBarIcon: function tabBarIconTemp({ focused }) {
          return <TabBarIcon focused={focused} tabType={0} />;
        },
        tabBarOnPress: ({ defaultHandler }) => {
          lastTabPosition = 0;
          defaultHandler();
          StatusBar.setBarStyle('light-content');
        },
      },
    },
    Merchant: {
      screen: MerchantList,
      navigationOptions: {
        tabBarLabel: '商户',
        tabBarIcon: function tabBarIconTemp({ focused }) {
          return <TabBarIcon focused={focused} tabType={1} />;
        },

        tabBarOnPress: ({ defaultHandler }) => {
          if (lastTabPosition !== 1) {
            DeviceEventEmitter.emit('switchMerchant');
          }
          lastTabPosition = 1;
          defaultHandler();
          StatusBar.setBarStyle('dark-content');
        },
      },
    },
    Mine: {
      screen: Mine,
      navigationOptions: {
        tabBarLabel: '我的',
        tabBarIcon: function tabBarIconTemp({ focused }) {
          return <TabBarIcon focused={focused} tabType={2} />;
        },
        tabBarOnPress: ({ defaultHandler }) => {
          lastTabPosition = 2;
          defaultHandler();
          StatusBar.setBarStyle('dark-content');
        },
      },
    },
  },
  {
    tabBarOptions: {
      showLabel: true,
      inactiveTintColor: '#BDCDE5',
      activeTintColor: '#009CFF',
      style: {
        backgroundColor: 'white',
        shadowColor: '#000',
        // alpha:0.04,
        shadowOffset: { width: 0, height: 10 },
        shadowOpacity: 0.5,
        shadowRadius: 10,
        elevation: 1,
        borderTopWidth: 0,
      },
      labelStyle: {
        top: -3,
      },
    },
    animationEnabled: true,
    lazy: false,
  }
);

const MainStack = createStackNavigator(
  {
    Tab,
    Search,
    ExtendList,
    ExtendContent,
    MerchantList,
    MerchantAdd,
    MerchantManage,
    MerchantDetail,
    MerchantAddSuccess,
    MerchantExceptionList,
    MerchantOperatorList,
    MerchantRateUpdate,
    PersonInfo,
    AboutMe,
    Setting,
    ActivateCode,
    QrCodeDetail,
    ScanCode,
    StoreList,
    QrCodeEdit,
    AdditionalInfo,
    BasicInfo,
    BankFuzzyQuery,
    BillingInfo,
    IdCardInfo,
    MaterialInfo,
    StorePhotos,
  },
  {
    headerMode: 'none',
    defaultNavigationOptions: {
      gesturesEnabled: false,
    },
    navigationOptions: () => ({
      header: null,
      gesturesEnabled: true,
    }),
  }
);

export const AppNavigator = createSwitchNavigator(
  {
    Splash,
    Guide,
    Auth,
    MainStack,
  },
  {
    initialRouteName: 'Splash',
    defaultNavigationOptions: {
      gesturesEnabled: ifAndroid(false, true),
    },
    headerMode: 'none',
  }
);

export function getCurrentScreen(navigationState) {
  if (!navigationState) {
    return null;
  }
  const route = navigationState.routes[navigationState.index];
  if (route.routes) {
    return getCurrentScreen(route);
  }
  return route.routeName;
}

export default AppNavigator;
BANG88 commented 5 years ago

Provider是怎么配置的。

Mageenz commented 5 years ago

@bang88 provider的配置是这样的:(appContainer.js)

  render() {
    return (
    <Provider>
      <Navigation
        // persistenceKey={navigationPersistenceKey}
        ref={v => {
          this.navigation = v;
        }}
      />
    </Provider>
    );
  }

整个文件的代码:

import React, { Component } from 'react';
import { BackHandler, NativeAppEventEmitter, StatusBar } from 'react-native';
import DeviceInfo from 'react-native-device-info';
import PushNotification from 'react-native-push-notification';
import { Provider } from '@ant-design/react-native';
import Navigation from './navigation/RootNav';
import navigator from './navigation/navigator';
import { isAndroid } from './utils';
import { store } from './index';

class AppContainer extends Component {
  constructor(props) {
    super(props);
    this.lastBackPressed = null;
    this.state = {
      id: '',
      name: '',
      contact: '',
      mobilePhone: '',
    };
  }

  componentDidMount() {
    if (isAndroid) {
      StatusBar.setTranslucent(true);
      StatusBar.setBackgroundColor('#00000022');
    }
    BackHandler.addEventListener('hardwareBackPress', this.backHandle);
    // this.initGetui();
  }

  componentWillUnmount() {
    BackHandler.removeEventListener('hardwareBackPress', this.backHandle);
  }

  backHandle = () => {
    // alert('如果看到这条弹框,告诉我');
    const currentScreen = navigator.getCurrentScreen();
    console.log(currentScreen, navigator, this.navigation);
    // navigator.pop();
    // this.navigation.dispatch(NavigationActions.back());
    // if (currentScreen !== 'Home' && currentScreen !== 'Login') {
    //   // this.props.dispatch(NavigationActions.back());
    //   navigator.pop();
    //   return true;
    // }
    // if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
    //   // 最近2秒内按过back键,可以退出应用。
    //   // BackAndroid.exitApp();
    //   return false;
    // }
    // this.lastBackPressed = Date.now();
    // ToastAndroid.show('再按一次退出应用', ToastAndroid.SHORT);
    return true;
  };

  initGetui() {
    NativeAppEventEmitter.addListener('receiveRemoteNotification', notification => {
      let dataJson;
      // Android的消息类型为payload 透传消息 或者 cmd消息
      switch (notification.type) {
        case 'cid':
          console.log(`receiveRemoteNotification cid = ${notification.cid}`);
          store.dispatch.auth.updateState({
            cid: notification.cid,
            uid: DeviceInfo.getUniqueID(),
          });
          break;
        case 'payload':
          // alert('payload');
          console.warn(JSON.stringify(notification));
          dataJson = JSON.parse(JSON.parse(notification.userInfo.payloadMsg).data);
          this.setState({
            id: dataJson.merchantId,
            name: dataJson.name,
            contact: dataJson.contact,
            mobilePhone: dataJson.mobilePhone,
          });
          // 如果离线,本地通知不弹出,如果在线,本地通知弹出
          if (notification.userInfo.offLine === 'NO') {
            PushNotification.localNotification({
              title: dataJson.title,
              message: dataJson.content,
            });
          }
          break;
        case 'cmd':
          // alert('cmd 消息通知', `cmd action = ${notification.cmd}`);
          break;
        case 'notificationArrived':
          // alert('notificationArrived 通知到达', JSON.stringify(notification));
          break;
        case 'notificationClicked':
          // alert('notificationArrived 通知点击', JSON.stringify(notification));
          break;
        default:
      }
    });

    NativeAppEventEmitter.addListener('clickRemoteNotification', () => {
      // console.warn('push'+this.state);
      console.warn(`ss${navigator.getCurrentScreen()}`);
      if (navigator) {
        if (navigator.getCurrentScreen() === 'MerchantManage') {
          console.warn('MerchantManage');
          navigator.replace('MerchantManage', {
            id: this.state.id,
            mobilePhone: this.state.mobilePhone,
            contact: this.state.contact,
            name: this.state.name,
          });
        } else {
          console.warn('ddddd');
        }
      } else {
        setTimeout(() => {
          if (navigator.getCurrentScreen() === 'MerchantManage') {
            navigator.replace('MerchantManage', {
              id: this.state.id,
              mobilePhone: this.state.mobilePhone,
              contact: this.state.contact,
              name: this.state.name,
            });
          } else {
            navigator.push('MerchantManage', {
              id: this.state.id,
              mobilePhone: this.state.mobilePhone,
              contact: this.state.contact,
              name: this.state.name,
            });
            console.warn('ddddd');
          }
        }, 2000);
      }
    });
  }

  render() {
    // const navigationPersistenceKey = __DEV__ ? 'NavigationStateDEV' : null;
    return (
      <Provider>
        <Navigation
          // persistenceKey={navigationPersistenceKey}
          ref={v => {
            this.navigation = v;
          }}
        />
      </Provider>
    );
  }
}

export default AppContainer;

appContainer还经过了一些处理:

以下是index.js的代码:

import React from 'react';
import createRematchPersist, { getPersistor } from '@rematch/persist';
import { PersistGate } from 'redux-persist/es/integration/react';
import { Provider } from 'react-redux';
import { createLogger } from 'redux-logger';
import storage from 'redux-persist/es/storage';
import createLoadingPlugin from '@rematch/loading';
import immerPlugin from '@rematch/immer';
import updatedPlugin from '@rematch/updated';
import selectorsPlugin from '@rematch/select';
import { init } from '@rematch/core';
import CodePush from 'react-native-code-push';
import AppContainer from './AppContainer';
import utilsMiddleware from './middleware/utilsMiddleware';
import getModels from './models';
import { ifAndroid } from './utils';
import Constants from './constants';

const isDebuggingInChrome = __DEV__ && !!window.navigator.userAgent;
const logger = createLogger({
  predicate: () => isDebuggingInChrome,
  collapsed: true,
  duration: true,
});
const persistPlugin = createRematchPersist({
  whitelist: ['user', 'auth'],
  storage,
  throttle: 5000,
  version: 1,
});
const loading = createLoadingPlugin();

const immer = immerPlugin();

const updated = updatedPlugin();

const select = selectorsPlugin();

const store = init({
  models: getModels(),
  redux: {
    // createStore: Reactotron.createStore,
    middlewares: [logger, utilsMiddleware],
  },
  plugins: [loading, persistPlugin, immer, updated, select],
});

const onBeforeLift = () => {
  // take some action before the gate lifts
};
const persistor = getPersistor();
const App = () => (
  <Provider store={store}>
    <PersistGate loading={null} onBeforeLift={onBeforeLift} persistor={persistor}>
      <AppContainer />
    </PersistGate>
  </Provider>
);

export { store };

const codePushOptions = {
  deploymentKey: __DEV__ ? '' : ifAndroid(Constants.CODE_PUSH_KEY_ANDROID, Constants.CODE_PUSH_KEY_IOS),
  checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME,
  installMode: CodePush.InstallMode.IMMEDIATE,
  updateDialog: {
    optionalIgnoreButtonLabel: '稍后', // 非强制更新时,取消按钮文字
    optionalInstallButtonLabel: '立即更新', // 非强制更新时,确认文字
    optionalUpdateMessage: '', // 非强制更新时,更新通知
    title: '更新提示', // 要显示的更新通知的标题
    mandatoryUpdateMessage: '', // 强制更新时,更新通知. Defaults to “An update is available that must be installed.”.
    mandatoryContinueButtonLabel: '确定', // 强制更新的按钮文字
    descriptionPrefix: '', // 更新说明的前缀。 默认是Description:
    appendReleaseDescription: true,
  },
};
const CodePushApp = CodePush(codePushOptions)(App);

export default CodePushApp;
BANG88 commented 5 years ago

跟 Ant 应该不影响的。 没看出来哪里有问题

Mageenz commented 5 years ago

算了,不纠结了。