路线“ ActivityFeed”的组件必须是React组件

时间:2019-07-24 19:27:10

标签: react-native react-navigation

我在SO上浏览过许多类似的帖子,涉及相似的问题,但是没有一个答案能为我解决。

这是完整的错误: enter image description here

因此在我的src/navigation/feed/stack.js中,其定义如下:

import React from 'react';
import {StackNavigator} from 'react-navigation';
import ActivityFeed from 'activity-feed/session-user/screens/Main';
import HamburgerButton from 'navigation-components/HamburgerButton';
import HeaderTitle from 'navigation-components/HeaderTitle';
import ActionAlertIndicator from 'navigation-components/ActionAlertIndicator';
import * as navConfig from '../config';
import * as cache from 'utils/cache';

const stack = StackNavigator(
  {
    ActivityFeed: {
      screen: ActivityFeed,
      navigationOptions: ({navigation}) => ({
        header: (
          <HeaderTitle
            headerLeft={() => (
              <HamburgerButton
                onPress={() => navigation.navigate('DrawerOpen')}
              />
            )}
            headerRight={() => (
              <ActionAlertIndicator
                onPress={() => {
                  cache.setRouteStarter('MainDrawer');
                  navigation.navigate('ActionAlertsStack');
                }}
              />
            )}
          />
        ),
      }),
    },
  },
  {
    navigationOptions: {
      ...navConfig.defaultStackConfig,
    },
  }
);

export default stack;

src/activity-feed/session-user/screens/Main.js内部定义实际的组件或屏幕:


import React, {PureComponent} from 'react';
import {
  FlatList,
  StyleSheet,
  AppState,
  Platform,
  Dimensions,
  View,
  Alert,
} from 'react-native';
import PropTypes from 'prop-types';
import OneSignal from 'react-native-onesignal';
import {Loading, SwippableCard, BottomAlert} from 'common-components';
import EmptyState from 'activity-feed/session-user/components/EmptyState';
import EventFeedCard from 'events/components/EventFeedCard';
import SurveyBallotFeedCard from 'surveys-ballots/components/FeedCard';
import MicroSurvey from 'surveys-ballots/components/MicroSurvey';
import ActionAlertFeedCard from 'action-alerts/components/ActionAlertFeedCard';
import MissingAddressCard from 'action-alerts/components/MissingAddressCard';
import ArticleFeedCard from 'articles/components/ArticleFeedCard';
import GetInvolvedFeedCard from 'account-settings/components/GetInvolvedFeedCard';
import {connect} from 'react-redux';
import {
  fetchFeed,
  handleContentSwipe,
  undoSwipeAction,
  hideUndoAlert,
} from 'activity-feed/actions';
import {setSelectedEvent} from 'events/actions';
import {setSelectedSurvey} from 'surveys-ballots/actions';
import {setSelectedAlert, getCampaignDetails} from 'action-alerts/actions';
import * as cache from 'utils/cache';
import {setSelectedArticle} from 'articles/actions';
import {
  handleUpdateTopics,
  handleUpdateGetInvoved,
} from 'account-settings/preferencesActions';
import {scale} from 'react-native-size-matters';
import {emptyStateStyles} from 'theme';

const {height} = Dimensions.get('window');

export class ActivityFeed extends PureComponent {
  static propTypes = {
    displayAlert: PropTypes.bool,
    feed: PropTypes.array,
    fetchFeed: PropTypes.func,
    getCampaignDetails: PropTypes.func,
    handleContentSwipe: PropTypes.func,
    handleUpdateGetInvoved: PropTypes.func,
    handleUpdateTopics: PropTypes.func,
    hideUndoAlert: PropTypes.func,
    lastSwippedElement: PropTypes.object,
    loading: PropTypes.bool,
    navigation: PropTypes.object,
    setSelectedAlert: PropTypes.func,
    setSelectedArticle: PropTypes.func,
    setSelectedEvent: PropTypes.func,
    setSelectedSurvey: PropTypes.func.isRequired,
    undoSwipeAction: PropTypes.func,
    userEmailIsValidForVoterVoice: PropTypes.bool,
  };

  constructor(props) {
    super(props);
    this.prompted = false;

    this.state = {
      refreshing: false,
      appState: AppState.currentState,
    };
  }

  async componentDidMount() {
    AppState.addEventListener('change', this._handleAppStateChange);
    if (!this.props.loading) {
      const doRefresh = await cache.shouldRefresh('feed');
      if (this.props.feed.length === 0 || doRefresh) {
        this.props.fetchFeed();
      }
      cache.incrementAppViews();
    }
  }

  componentWillUnmount() {
    AppState.removeEventListener('change', this._handleAppStateChange);
  }

  _handleAppStateChange = async appState => {
    if (
      this.state.appState.match(/inactive|background/) &&
      appState === 'active'
    ) {
      cache.incrementAppViews();
      const doRefresh = await cache.shouldRefresh('feed');
      if (doRefresh) {
        this.props.fetchFeed();
      }
    }
    this.setState({appState});
  };

  _keyExtractor = ({Entity}) =>
    (Entity.Key || Entity.Id || Entity.CampaignId || Entity.Code).toString();

  _gotoEvent = event => {
    cache.setRouteStarter('MainDrawer');
    this.props.setSelectedEvent(event);
    const title = `${event.LegislatureType} Event`;
    this.props.navigation.navigate('EventDetails', {title});
  };

  _gotoSurveyBallot = survey => {
    cache.setRouteStarter('MainDrawer');
    this.props.setSelectedSurvey(survey);
    this.props.navigation.navigate('SurveyDetails');
  };

  _gotoArticle = article => {
    cache.setRouteStarter('MainDrawer');
    this.props.setSelectedArticle(article);
    this.props.navigation.navigate('ArticleDetails');
  };

  _onAlertActionButtonPress = async item => {
    cache.setRouteStarter('MainDrawer');
    await this.props.setSelectedAlert(item.Entity);
    this.props.getCampaignDetails();
    if (this.props.userEmailIsValidForVoterVoice) {
      this.props.navigation.navigate('Questionnaire');
    } else {
      this.props.navigation.navigate('UnconfirmedEmail');
    }
  };

  _onSwipedOut = (swippedItem, index) => {
    this.props.handleContentSwipe(this.props, {swippedItem, index});
  };

  _handleGetInvolved = (response, entity) => {
    if (response !== entity.IsSelected) {
      const isTopic = entity.Category !== 'GetInvolved';
      const items = [
        {
          ...entity,
          IsSelected: response,
        },
      ];
      if (isTopic) {
        this.props.handleUpdateTopics({topics: items});
      } else {
        this.props.handleUpdateGetInvoved({involved: items});
      }
    }
  };

  renderItem = ({item, index}) => {
    const {Type, Entity} = item;
    if (Type === 'EVENT') {
      return (
        <SwippableCard onSwipedOut={() => this._onSwipedOut(item, index)}>
          <EventFeedCard
            style={styles.push}
            mainActionButtonPress={() => this._gotoEvent(Entity)}
            event={Entity}
          />
        </SwippableCard>
      );
    }

    if (['SURVEY_SURVEY', 'SURVEY_BALLOT'].includes(Type)) {
      return (
        <SwippableCard onSwipedOut={() => this._onSwipedOut(item, index)}>
          <SurveyBallotFeedCard
            style={styles.push}
            survey={Entity}
            handleViewDetails={() => this._gotoSurveyBallot(Entity)}
          />
        </SwippableCard>
      );
    }

    if (Type === 'SURVEY_MICRO') {
      return (
        <SwippableCard onSwipedOut={() => this._onSwipedOut(item, index)}>
          <MicroSurvey style={styles.push} selectedSurvey={Entity} />
        </SwippableCard>
      );
    }

    if (Type === 'ALERT') {
      return (
        <SwippableCard onSwipedOut={() => this._onSwipedOut(item, index)}>
          <ActionAlertFeedCard
            datePosted={Entity.StartDateUtc}
            style={styles.push}
            title={Entity.Headline}
            content={Entity.Alert}
            mainActionButtonPress={() => this._onAlertActionButtonPress(item)}
            secondaryActionButtonPress={() => {
              this.props.setSelectedAlert(Entity);
              // eslint-disable-next-line
              this.props.navigation.navigate("ActionAlertDetails", {
                content: Entity.Alert,
                id: Entity.CampaignId,
                title: Entity.Headline,
              });
            }}
          />
        </SwippableCard>
      );
    }

    if (Type === 'ARTICLE') {
      return (
        <SwippableCard onSwipedOut={() => this._onSwipedOut(item, index)}>
          <ArticleFeedCard
            content={Entity}
            style={styles.push}
            mainActionButtonPress={() => this._gotoArticle(Entity)}
          />
        </SwippableCard>
      );
    }

    //prettier-ignore
    if (Type === 'NOTIFICATION' && Entity.Code === 'INDIVIDUAL_ADDRESS_HOME_MISSING') {
      return (
        <MissingAddressCard
          style={styles.push}
          navigate={() => this.props.navigation.navigate('HomeAddress')}
        />
      );
    }

    if (['PREFERENCE_TOPIC', 'PREFERENCE_INVOLVEMENT'].includes(Type)) {
      return (
        <SwippableCard onSwipedOut={() => this._onSwipedOut(item, index)}>
          <GetInvolvedFeedCard
            style={styles.push}
            title={Entity.DisplayText}
            onPress={response => this._handleGetInvolved(response, Entity)}
          />
        </SwippableCard>
      );
    }

    return null;
  };

  _onRefresh = async () => {
    try {
      this.setState({refreshing: true});
      this.props
        .fetchFeed()
        .then(() => {
          this.setState({refreshing: false});
        })
        .catch(() => {
          this.setState({refreshing: false});
        });
    } catch (e) {
      this.setState({refreshing: false});
    }
  };

  _trackScroll = async event => {
    try {
      if (this.prompted) {
        return;
      }

      const y = event.nativeEvent.contentOffset.y;
      const scrollHeight = height * 0.8;
      const page = Math.round(Math.floor(y) / scrollHeight);
      const alert = await cache.shouldPromtpPushNotificationPermissions();
      const iOS = Platform.OS === 'ios';

      if (alert && iOS && page > 1) {
        this.prompted = true;
        this._openPromptAlert();
      }
    } catch (e) {
      return false;
    }
  };

  _openPromptAlert = () => {
    Alert.alert(
      'Push Notifications Access',
      'Stay engaged with NFIB on the issues and activities you care about by allowing us to notify you using push notifications',
      [
        {
          text: 'Deny',
          onPress: () => {
            cache.pushNotificationsPrompted();
          },
          style: 'cancel',
        },
        {
          text: 'Allow',
          onPress: () => {
            OneSignal.registerForPushNotifications();
            cache.pushNotificationsPrompted();
          },
        },
      ],
      {cancelable: false}
    );
  };

  _getAlertTitle = () => {
    const {lastSwippedElement} = this.props;
    const {Type} = lastSwippedElement.swippedItem;

    if (Type.startsWith('PREFERENCE')) {
      return 'Preference Dismissed';
    }

    switch (Type) {
      case 'EVENT':
        return 'Event Dismissed';
      case 'SURVEY_BALLOT':
        return 'Ballot Dismissed';
      case 'SURVEY_SURVEY':
        return 'Survey Dismissed';
      case 'SURVEY_MICRO':
        return 'Micro Survey Dismissed';
      case 'ARTICLE':
        return 'Article Dismissed';
      case 'ALERT':
        return 'Action Alert Dismissed';
      default:
        return 'Dismissed';
    }
  };

  render() {
    if (this.props.loading && !this.state.refreshing) {
      return <Loading />;
    }

    const contentStyles =
      this.props.feed.length > 0 ? styles.content : emptyStateStyles.container;

    return (
      <View style={styles.container}>
        <FlatList
          contentContainerStyle={contentStyles}
          showsVerticalScrollIndicator={false}
          data={this.props.feed}
          renderItem={this.renderItem}
          keyExtractor={this._keyExtractor}
          removeClippedSubviews={false}
          onRefresh={this._onRefresh}
          refreshing={this.state.refreshing}
          ListEmptyComponent={() => (
            <EmptyState navigation={this.props.navigation} />
          )}
          scrollEventThrottle={100}
          onScroll={this._trackScroll}
        />
        {this.props.displayAlert && (
          <BottomAlert
            title={this._getAlertTitle()}
            onPress={this.props.undoSwipeAction}
            hideAlert={this.props.hideUndoAlert}
          />
        )}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  content: {
    paddingHorizontal: scale(8),
    paddingTop: scale(16),
    paddingBottom: scale(20),
  },
  push: {
    marginBottom: 16,
  },
});

const mapState2Props = ({
  activityFeed,
  auth: {userEmailIsValidForVoterVoice},
  navigation,
}) => {
  return {
    ...activityFeed,
    userEmailIsValidForVoterVoice,
    loading: activityFeed.loading || navigation.deepLinkLoading,
  };
};

export default connect(mapState2Props, {
  fetchFeed,
  getCampaignDetails,
  handleUpdateGetInvoved,
  handleUpdateTopics,
  setSelectedAlert,
  setSelectedArticle,
  setSelectedEvent,
  setSelectedSurvey,
  handleContentSwipe,
  undoSwipeAction,
  hideUndoAlert,
})(ActivityFeed);

我的代码看不到任何东西,我想知道react-navigation团队是否进行了某些更改。

我使用的react-navigation版本1.5.11和react-native的版本0.60.4。

这是RN版本的兼容性问题吗?除了升级,我别无选择吗?

2 个答案:

答案 0 :(得分:2)

眼前的问题看起来像组件文件中的多个导出。尝试在类定义之前删除export,仅将export default保留在末尾。

关于SetNewPassword.jsCompleteAccount.js具有相同导出的困惑,只要导入default exported组件,就不会造成问题。

简单地说,

如果将组件导出为default,则可以导入而无需使用{}大括号,例如

import CompleteAccount from '.../CompleteAccount.js'

在这里您可以为导入文件命名。

如果使用花括号,则将导入named export,例如

import {CompleteAccount} from '.../CompleteAccount.js'

答案 1 :(得分:2)

经过6天的辛苦工作,并尝试了一些与我们对使用命名括号(当您将其命名为export时使用花括号)相反的理解,我始终怀疑问题出在react-navigation上,因为我没有弄乱react-navigation版本或代码库。

问题是react-navigationreact-redux版本7中如何工作。

React Navigation无法识别React-Redux版本7返回的对象。

解决方案是降级到React-Redux版本5.1.1。