我需要将此this.props.navigation传递给一个辅助函数。但是,我目前正坐在根页面上,该页面最初创建了stackNavigator。到目前为止,我已经尝试从react-navigation导入Navigator,NavigatorActions和withNavigation,然后将其作为参数发送到我的辅助函数中,但是我一直收到错误消息:无法读取未定义的属性“ state”。>
以下是与我相关的RootPage代码:
/**
* RootPage has the Navigator. So it controls navigation.
*/
import React, { Component } from 'react';
import {
Platform,
BackHandler,
View,
Animated,
StyleSheet,
Image,
TouchableWithoutFeedback,
TouchableOpacity,
Alert
} from 'react-native';
import { connect } from 'react-redux';
import { BoxShadow } from 'react-native-shadow';
import { NavigationActions } from 'react-navigation';
import { vsprintf } from 'sprintf-js';
import RootNavigator from './RootNavigator';
//Test Component
import TestPageFactory from '../TestPage/TestPageFactory';
// Components
import XXBluetoothManager, { XXBluetoothEventEmitter } from '../../nativeComponents/XXBluetooth/XXBluetooth';
import XXText from '../../components/XXText/XXRobotoText';
import XXSyncXXerlay from '../../components/XXSyncXXerlay/XXSyncXXerlay';
// Actions
import { logout } from '../../actions/user';
import { closeSyncModal, toggleRootMenu, openRootMenu, closeRootMenu } from '../../actions/page';
// Utils
import { LAYOUTINFO } from '../../utils/layoutinfo';
import { generateElevationStyle } from '../../utils/util';
import { NavUtils } from '../../utils/navutils';
import { StatusBarManager } from '../../utils/statusbarmanager';
// Config
import { DEVICEINFO } from '../../config';
// Localization
import I18n from '../../localization/i18n';
import { aDeviceHasBeenConnectedFunc } from '../../actions/device';
// Constants
const DEFAULT_PROFILE_IMAGE = require('../../images/profile-image/user.png');
const HEIGHT_RATIO = DEVICEINFO.IS_EXTRA_SMALL_SCREEN ? 0.5 : (DEVICEINFO.SCREEN_HEIGHT >= 667 ? (DEVICEINFO.SCREEN_HEIGHT / 667) : 0.7);
class RootPage extends Component {
static getDerivedStateFromProps(nextProps, prevState) {
let shouldLoadingBeVisible = nextProps.pageStatus && nextProps.pageStatus.isUpdateRequested ? true : false;
if (!nextProps.profile && shouldLoadingBeVisible === prevState.loadingVisible) {
return null;
}
return {
profileImage: nextProps.profile && nextProps.profile.profileImage ?
{ uri: nextProps.profile.profileImage } : DEFAULT_PROFILE_IMAGE,
profileName: nextProps.profile && nextProps.profile.profileName ? nextProps.profile.profileName : '',
loadingVisible: shouldLoadingBeVisible,
};
}
constructor(props) {
super(props);
this.routeStatusBarStyle = 'dark-content';
this.state = {
animatedPosition: new Animated.ValueXY(0, 0),
profileImage: DEFAULT_PROFILE_IMAGE,
profileName: '',
deviceAddress: undefined,
isMenuVisible: false,
loggingOut: false,
device: undefined,
};
}
componentDidMount() {
if (Platform.OS === 'android') {
this.initAndroidHardwareBackEvent();
}
// this.initializeBluetooth();
}
componentWillUnmount() {
this.destroyBluetooth();
}
componentDidUpdate(prevProps, prevState) {
if (this.state.loggingOut) {
if (this.props.signinStatus && !this.props.signinStatus.isLoginSuccess) {
this.setState({ loggingOut: false }, () => {
this.onPressMenu('EntryPage');
});
}
}
if (this.props.pageStatus.isMenuVisible != null && prevState.isMenuVisible !== this.props.pageStatus.isMenuVisible) {
this.setState({ isMenuVisible: this.props.pageStatus.isMenuVisible }, () => {
this.onUpdateMenu(this.props.pageStatus.isMenuVisible);
});
}
// console.log('what are the prev state here? ", ', prevState)
/*
NOTE: We need to know when an item has been connected, if an item has not been connected, we should
make the hearing test available
*/
}
render() {
return (
<View style={styles.rootView}>
{this.renderRootMenu()}
<Animated.View style={[styles.animatedView, { left: this.state.animatedPosition.x, top: this.state.animatedPosition.y }]}>
{this.renderXXerlay()}
{this.renderNavigator()}
</Animated.View>
{this.renderSyncXXerlay()}
</View>
);
}
updateStatusBarStyle(pageId) {
let newStatusBarStyle = 'dark-content';
if (pageId === 'EntryPage') {
newStatusBarStyle = 'light-content';
}
if (newStatusBarStyle !== this.routeStatusBarStyle) {
this.routeStatusBarStyle = newStatusBarStyle;
StatusBarManager.setStyle(newStatusBarStyle, false)
}
}
renderRootMenu() {
return (
<View style={styles.menuView}>
{this.renderMenuTopArea()}
{this.renderMenuBottomArea()}
</View>
)
}
renderMenuTopArea() {
if (DEVICEINFO.IS_ANDROID && (Platform.Version === 19 || Platform.Version === 20)) {
return this.renderMenuTopAreaKitKat();
}
return this.renderMenuTopAreaDefault();
}
renderMenuTopAreaDefault() {
return (
<View style={[styles.menuTopArea, generateElevationStyle(1.5)]}>
{this.renderProfileImage()}
</View>
)
}
renderMenuTopAreaKitKat() {
let shadowOptions = {
width: DEVICEINFO.SCREEN_WIDTH,
height: (DEVICEINFO.SCREEN_HEIGHT * 0.35) + 2,
color: '#000',
border: 2,
opacity: 0.05,
radius: 1,
y: -2.5,
};
return (
<BoxShadow setting={shadowOptions}>
<View style={styles.menuTopArea}>
{this.renderProfileImage()}
</View>
</BoxShadow>
)
}
renderMenuBottomArea() {
return (
<View style={styles.menuBottomArea}>
{this.renderMenuContents()}
</View>
)
}
renderMenuContents() {
const menuData = this.generateMenuData();
let renderOutput = [];
for (let i = 0, len = menuData.length; i < len; i++) {
if (this.state.deviceAddress && menuData[i].onlyNotConnected) {
continue;
}
let extraStyle = {};
if (i === 0) {
let extraStyleMarginTop = DEVICEINFO.IS_EXTRA_SMALL_SCREEN ? 16 : 26;
extraStyle['marginTop'] = extraStyleMarginTop * LAYOUTINFO.DESIGN_HEIGHT_RATIO;
}
renderOutput.push(
<TouchableOpacity
key={menuData[i].text}
activeOpacity={0.8}
underlayColor='transparent'
onPress={() => { menuData[i].onPressMenu() }}
style={[styles.menuButtonTouch, extraStyle]}>
<View style={styles.menuButtonView}>
<Image
source={menuData[i].icon}
style={[styles.menuIcon]} />
<XXText style={[styles.menuText]}>{menuData[i].text}</XXText>
</View>
</TouchableOpacity>
)
}
return renderOutput;
}
generateMenuData() {
return [
this.generateMenuItem(I18n.t('home'), require('../../images/menu-home.png'), 'HomePage'),
this.generateMenuItem(I18n.t('tutorial'), require('../../images/menu-tutorial.png'), 'SelectTutorialPage'),
this.generateMenuItem(I18n.t('item_list'), require('../../images/menu-add-item.png'), 'itemListPage'),
this.generateMenuItem(I18n.t('report'), require('../../images/menu-report.png'), 'ReportDetailPage'),
this.generateTestPageOnly(I18n.t('hearing_test'), require('../../images/menu-demotest.png')),
this.generateMenuItem(I18n.t('equalizer'), require('../../images/menu-sound.png'), 'SoundPage'),
this.generateMenuItem(I18n.t('support'), require('../../images/menu-support.png'), 'SupportPage'),
this.generateMenuItem(I18n.t('account_settings'), require('../../images/menu-settings.png'), 'SettingsPage'),
{
text: I18n.t('logout'),
icon: require('../../images/menu-logout.png'),
onPressMenu: () => {
Alert.alert(
I18n.t('item'),
I18n.t('logout_confirmation'),
[
{ text: I18n.t('cancel'), onPress: () => { } },
{ text: I18n.t('logout'), onPress: () => { this.onPressLogout() } }
]
)
}
},
];
}
generateTestPageOnly(label, icon) {
let deviceAddress;
let versionData;
// console.log('what is the props here: ', this.props.devices)
function loopThruObject(objectOfDevices) {
for (let [key, value] of Object.entries(objectOfDevices)) {
for (let [newKey, newValue] of Object.entries(value)) {
if (newKey === 'macAddress') {
deviceAddress = newValue;
}
if (newKey === 'version')
versionData = newValue
}
}
return;
}
loopThruObject(this.props.devices)
let currentDevice = this.props.devices[deviceAddress];
let newParams = {
navIndex: 1,
device: currentDevice
};
if (this.props.aDeviceHasBeenConnected === true) {
return {
text: label,
icon: icon,
onPressMenu: () => {
let testPageIdentifier = TestPageFactory.getIdentifier({ isDemo: false, versionData: versionData });
console.log('what is the testPage identifier: ', this.props.navigation)
// NavUtils.push(NavigationActions, testPageIdentifier, { device: currentDevice });
NavigationActions.dispatch({ type: 'Navigation/NAVIGATE', routeName: 'FittingTestPage', params: newParams })
}
}
}
if (this.props.aDeviceHasBeenConnected === false) {
return {
text: 'N/A',
icon: icon,
onPressMenu: () => {
}
}
}
}
generateMenuItem(label, icon, onPressId, onlyNotConnected = false) {
return {
text: label,
icon: icon,
onPressMenu: () => {
this.onPressMenu(onPressId);
},
onlyNotConnected
}
}
renderProfileImage() {
imageSource = this.state.profileImage;
let deviceCount = Object.keys(this.props.devices).length;
let deviceConnectionString = this.generateConnectedString(deviceCount);
return (
<View style={styles.profileImageSubContainer}>
<Image
key={this.state.profileImage.uri}
source={imageSource}
style={styles.profileImage} />
{/* <XXText style={[styles.profileText]} fontWeight='Regular'>{this.state.profileName}</XXText> */}
<XXText style={[styles.deviceText]} fontWeight={'Light'}>{deviceConnectionString}</XXText>
</View>
)
}
generateConnectedString(deviceCount) {
let deviceConnectionString;
if (deviceCount === 1) {
deviceConnectionString = vsprintf(I18n.t('one_item_connected_format_str'), deviceCount.toString());
}
else {
deviceConnectionString = vsprintf(I18n.t('items_connected_format_str'), deviceCount.toString());
}
return deviceConnectionString;
}
renderNavigator() {
return (
<RootNavigator
ref={nav => {
this.navigator = nav;
}}
onNavigationStateChange={(prevState, currentState) => {
this.handleNavSceneChange(prevState, currentState);
}} />
)
}
handleNavSceneChange(prevState, currentState) {
const currentScreen = NavUtils.getCurrentRouteNameFromState(currentState);
this.currentPageId = currentScreen;
this.updateStatusBarStyle(currentScreen);
}
renderXXerlay() {
// Workaround for Android position absolute bug
// issue : https://github.com/facebook/react-native/issues/8923
let visible = this.state.isMenuVisible;
return (
<TouchableWithoutFeedback
onPress={() => { this.hideMenu() }}>
<View style={[styles.XXerlayView, { height: visible ? null : 0 }]} />
</TouchableWithoutFeedback>
)
}
renderSyncXXerlay() {
let visible = this.props.pageStatus.syncModal;
if (!visible) {
return null;
}
return (
<XXSyncXXerlay
visible={visible} />
)
}
//
// EVENTS
//
onPressMenu(pageId) {
requestAnimationFrame(() => {
if (this.currentPageId !== pageId) {
if (pageId === 'DemoTestPage') {
NavUtils.push(this.navigator, pageId);
}
else {
NavUtils.resetTo(this.navigator, pageId);
}
this.updateStatusBarStyle(pageId);
}
this.hideMenu();
});
}
onPressLogout() {
this.setState({ loggingOut: true }, () => {
this.props.logout();
});
}
onUpdateMenu(isMenuVisible) {
if (isMenuVisible === undefined || this.previousMenuVisible === isMenuVisible) {
return;
}
this.previousMenuVisible = isMenuVisible;
// When menu needs to be opened
if (isMenuVisible) {
StatusBarManager.setStyle('light-content', isMenuVisible);
Animated.timing(
this.state.animatedPosition,
{
tXXalue: {
// Animate the RootPage to the right.
// x: 317 / 375 * DEVICEINFO.SCREEN_WIDTH,
// y: 126 / 667 * DEVICEINFO.SCREEN_HEIGHT,
x: 317 / 375 * DEVICEINFO.SCREEN_WIDTH,
y: 1 / 1000 * DEVICEINFO.SCREEN_HEIGHT
},
// duration: 300
// debugging mode
duration: 300
}
).start();
return;
}
// When menu needs to be closed
StatusBarManager.setStyle(this.routeStatusBarStyle, isMenuVisible);
Animated.timing(
this.state.animatedPosition,
{
tXXalue: {
x: 0,
y: 0
},
duration: 300
}
).start();
}
//
// Methods
//
initializeBluetooth() {
// Event listeners for XXBluetoothManager
this.itemDeviceStatusChanged = XXBluetoothEventEmitter.addListener('itemDeviceStatusChanged', event => {
// console.log('ROotpage; . initializeBluetooth: ', event)
this.getConnectedDevice();
});
this.getConnectedDevice();
}
destroyBluetooth() {
if (this.itemDeviceStatusChanged) {
this.itemDeviceStatusChanged.remXXe();
}
}
getConnectedDevice() {
console.log('when does getconnectedDevice get fired inside rootpage? :D', this.props)
XXBluetoothManager.getDeviceAddress((error, address) => {
if (error) {
// Not connected
this.setState({ deviceAddress: null });
return;
}
// console.log('when can we read error, address', error, address)
this.setState({ deviceAddress: address });
});
}
initAndroidHardwareBackEvent() {
BackHandler.addEventListener('hardwareBackPress', () => {
if (!this.navigator) {
return false;
}
if (this.navigator.state.index > 0) {
const backAction = NavigationActions.back({
key: null
});
this.navigator.dispatch(backAction);
return true;
}
return false;
});
}
toggleMenu() {
this.props.toggleRootMenu();
}
hideMenu() {
this.props.closeRootMenu();
}
}
const mapStateToProps = (state) => {
return {
signinStatus: state.page.signin,
pageStatus: state.page.root,
profile: state.user.profile,
devices: state.device.devices,
aDeviceHasBeenConnected: state.device.aDeviceHasBeenConnected,
}
}
const mapDispatchToProps = (dispatch) => {
return {
logout: () => {
dispatch(logout());
},
closeSyncModal: () => {
dispatch(closeSyncModal());
},
toggleRootMenu: () => {
dispatch(toggleRootMenu());
},
openRootMenu: () => {
dispatch(openRootMenu());
},
closeRootMenu: () => {
dispatch(closeRootMenu());
},
}
}
export default connect(mapStateToProps, mapDispatchToProps)(RootPage);
const styles = StyleSheet.create({
rootView: {
flex: 1,
flexDirection: 'column',
},
menuView: {
width: DEVICEINFO.SCREEN_WIDTH,
height: '100%',
backgroundColor: '#aab942',
position: 'absolute',
top: 0,
left: 0
},
menuTopArea: {
backgroundColor: '#b7c846',
// height: DEVICEINFO.SCREEN_HEIGHT * 0.35,
height: DEVICEINFO.SCREEN_HEIGHT * 0.28,
width: '100%',
flexDirection: 'column',
},
menuBottomArea: {
// backgroundColor: '#aab942',
width: '100%',
flex: 1,
flexDirection: 'column',
},
animatedView: {
position: 'relative',
flex: 1,
flexDirection: 'column',
backgroundColor: '#fff'
},
profileImageSubContainer: {
height: '100%',
flexDirection: 'column',
justifyContent: 'center',
marginLeft: 40,
marginTop: 10,
},
profileImage: {
width: DEVICEINFO.IS_EXTRA_SMALL_SCREEN ? 70 : 80,
height: DEVICEINFO.IS_EXTRA_SMALL_SCREEN ? 70 : 80,
borderRadius: (DEVICEINFO.IS_EXTRA_SMALL_SCREEN ? 70 : 80) / 2
},
profileText: {
marginTop: DEVICEINFO.IS_EXTRA_SMALL_SCREEN ? 6 : 16,
fontSize: Math.min(LAYOUTINFO.DESIGN_WIDTH_RATIO * 20, 21),
color: '#fff',
backgroundColor: 'transparent'
},
deviceText: {
marginTop: DEVICEINFO.IS_EXTRA_SMALL_SCREEN ? 6 : 16,
fontSize: Math.min(LAYOUTINFO.DESIGN_WIDTH_RATIO * 14, 15),
color: '#fff',
backgroundColor: 'transparent'
},
menuContainer: {
flex: 1,
width: '100%',
height: '100%',
flexDirection: 'column',
alignItems: 'flex-start',
},
// menubutton
menuButtonTouch: {
marginTop: 25 * HEIGHT_RATIO,
marginLeft: 38,
},
menuButtonView: {
flexDirection: 'row',
alignItems: 'center'
},
menuIcon: {
width: 18,
height: 18,
resizeMode: 'contain'
},
// menubuttontext
menuText: {
marginLeft: 17,
fontSize: Math.min(LAYOUTINFO.DESIGN_WIDTH_RATIO * 22, 22),
color: '#fff'
},
XXerlayView: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
width: null,
height: null,
backgroundColor: 'rgba( 255, 255, 255, 0.0001 )',
zIndex: 9999
}
});
这是RootNavigator页面
import { Platform } from 'react-native';
import { StackNavigator } from 'react-navigation';
// Pages
import EntryPage from '../EntryPage/EntryPage';
import SigninPage from '../SigninPage/SigninPage';
import SignupPage from '../SignupPage/SignupPage';
import ReportSinglePage from '../ReportSinglePage/ReportSinglePage';
import HomePage from '../HomePage/HomePage';
import VolumePage from '../VolumePage/VolumePage';
import ReportPage from '../ReportPage/ReportPage';
import ReportDetailPage from '../ReportDetailPage/ReportDetailPage';
import ItemListPage from '../ItemListPage/ItemListPage';
import AddItemPage from '../AddItemPage/AddItemPage';
import SupportPage from '../SupportPage/SupportPage';
import SettingsPage from '../SettingsPage/SettingsPage';
import SoundPage from '../SoundPage/SoundPage';
import VerticalSoundPage from '../SoundPage/VerticalSoundPage';
import SoundSyncPage from '../SoundSyncPage/SoundSyncPage';
import TestPage from '../TestPage/TestPage';
import iOSTestPage from '../TestPage/iOSTestPage';
import FittingTestPage from '../TestPage/FittingTestPage';
import SendResultSlide from '../SoundSyncPage/SendResultSlide';
import ModePage from '../ModePage/ModePage';
import PolicyPage from '../PolicyPage/PolicyPage';
import PrivacyPage from '../PrivacyPage/PrivacyPage';
import TermsAndConditionsPage from '../TermsAndConditionsPage/TermsAndConditionsPage';
import SelectTutorialPage from '../TutorialPage/SelectTutorialPage';
import PreparingTutorial from '../TutorialPage/PreparingTutorial';
import AddItemTutorial from '../TutorialPage/AddItemTutorial';
import HearingTestTutorial from '../TutorialPage/HearingTestTutorial';
import ReportTutorial from '../TutorialPage/ReportTutorial';
import EqualizerTutorial from '../TutorialPage/EqualizerTutorial';
import AddItemTutorialCompletion from '../TutorialPage/AddItemTutorialCompletion';
import AddItemTutorialFailure from '../TutorialPage/AddItemTutorialFailure';
import HearingTestTutorialCompletion from '../TutorialPage/HearingTestTutorialCompletion';
// import RootPage from '../RootPage/RootPage';
const RootNavigator = StackNavigator(
{
// RootPage: {screen: RootPage},
EntryPage: { screen: EntryPage },
SignupPage: { screen: SignupPage },
HomePage: { screen: HomePage },
TestPage: { screen: TestPage },
iOSTestPage: { screen: iOSTestPage },
FittingTestPage: { screen: FittingTestPage },
ReportPage: { screen: ReportPage },
ReportDetailPage: { screen: ReportDetailPage },
ReportSinglePage: { screen: ReportSinglePage },
ItemListPage: { screen: ItemListPage },
AddItemPage: { screen: AddItemPage },
SoundPage: { screen: VerticalSoundPage },
SoundSyncPage: { screen: SoundSyncPage },
SupportPage: { screen: SupportPage },
SettingsPage: { screen: SettingsPage },
VolumePage: { screen: VolumePage },
SendResultSlide: { screen: SendResultSlide },
PolicyPage: { screen: PolicyPage },
PrivacyPage: { screen: PrivacyPage },
TermsAndConditionsPage: { screen: TermsAndConditionsPage },
SelectTutorialPage: { screen: SelectTutorialPage },
PreparingTutorial: { screen: PreparingTutorial },
AddItemTutorial: { screen: AddItemTutorial },
HearingTestTutorial: { screen: HearingTestTutorial },
ReportTutorial: { screen: ReportTutorial },
EqualizerTutorial: { screen: EqualizerTutorial },
AddItemTutorialCompletion: { screen: AddItemTutorialCompletion },
AddItemTutorialFailure: { screen: AddItemTutorialFailure },
HearingTestTutorialCompletion: { screen: HearingTestTutorialCompletion },
},
{
initialRouteName: 'EntryPage',
navigationOptions: {
header: null,
}
},
{
headerMode: 'none'
}
);
export default RootNavigator;
以防万一,这是我要使用的辅助函数:
static push(navigator, pageId, params = {}) {
let newParams = { navIndex: 1 };
if (navigator.state.index) {
newParams.navIndex = navigator.state.index + 1;
}
else if (navigator.state.params && navigator.state.params.navIndex) {
newParams.navIndex = navigator.state.params.navIndex + 1;
}
Object.assign(newParams, params);
navigator.dispatch({ type: 'Navigation/NAVIGATE', routeName: pageId, params: newParams });
}