路由“ActivityFeed"的组件必须是 React 组件 [英] The component for route 'ActivityFeed' must be a React component

查看:47
本文介绍了路由“ActivityFeed"的组件必须是 React 组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于类似的问题,我在 SO 上浏览了各种类似的帖子,但没有一个答案为我解决.

这是完整的错误:

所以在我的 src/navigation/feed/stack.js 中,它的定义如下:

从'react'导入React;从反应导航"导入 {StackNavigator};从 'activity-feed/session-user/screens/Main' 导入 ActivityFeed;从 'navigation-components/HamburgerButton' 导入 HamburgerButton;从 'navigation-components/HeaderTitle' 导入 HeaderTitle;从 'navigation-components/ActionAlertIndicator' 导入 ActionAlertIndicator;import * as navConfig from '../config';从 'utils/cache' 导入 * 作为缓存;const stack = StackNavigator({活动源:{屏幕:ActivityFeed,导航选项:({导航})=>({标题:(<标题headerLeft={() =>(<汉堡按钮onPress={() =>navigation.navigate('DrawerOpen')}/>)}headerRight={() =>(<ActionAlertIndicatoronPress={() =>{cache.setRouteStarter('MainDrawer');navigation.navigate('ActionAlertsStack');}}/>)}/>),}),},},{导航选项:{...navConfig.defaultStackConfig,},});导出默认堆栈;

实际的组件或屏幕在 src/activity-feed/session-user/screens/Main.js 中像这样定义:

从反应"导入反应,{PureComponent};进口 {平面列表,样式表,应用状态,平台,方面,看法,警报,来自'反应原生';从 'prop-types' 导入 PropTypes;从'react-native-onesignal'导入OneSignal;从'common-components'导入{Loading,SwippableCard,BottomAlert};从'activity-feed/session-user/components/EmptyState'导入EmptyState;从事件/组件/EventFeedCard"导入 EventFeedCard;从'surveys-ballots/components/FeedCard'导入SurveyBallotFeedCard;从 'surveys-ballots/components/MicroSurvey' 导入 MicroSurvey;从'action-alerts/components/ActionAlertFeedCard'导入ActionAlertFeedCard;从'action-alerts/components/MissingAddressCard'导入MissingAddressCard;从 'articles/components/ArticleFeedCard' 导入 ArticleFeedCard;从帐户设置/组件/GetInvolvedFeedCard"导入 GetInvolvedFeedCard;从'react-redux'导入{connect};进口 {获取饲料,处理内容滑动,撤销滑动操作,隐藏撤消警报,来自活动源/动作";从事件/动作"导入 {setSelectedEvent};从 'surveys-ballots/actions' 导入 {setSelectedSurvey};从'action-alerts/actions'导入{setSelectedAlert,getCampaignDetails};从 'utils/cache' 导入 * 作为缓存;从文章/操作"导入 {setSelectedArticle};进口 {处理更新主题,handleUpdateGetInvoved,来自帐户设置/首选项操作";从'react-native-size-matters'导入{scale};从主题"导入 {emptyStateStyles};const {height} = Dimensions.get('window');导出类 ActivityFeed 扩展 PureComponent {静态 propTypes = {displayAlert: PropTypes.bool,提要:PropTypes.array,fetchFeed: PropTypes.func,getCampaignDetails: PropTypes.func,handleContentSwipe: PropTypes.func,handleUpdateGetInvoved: PropTypes.func,handleUpdateTopics: PropTypes.func,hideUndoAlert: PropTypes.func,lastSwippedElement: PropTypes.object,加载:PropTypes.bool,导航:PropTypes.object,setSelectedAlert: PropTypes.func,setSelectedArticle: PropTypes.func,setSelectedEvent: PropTypes.func,setSelectedSurvey: PropTypes.func.isRequired,undoSwipeAction: PropTypes.func,userEmailIsValidForVoterVoice: PropTypes.bool,};构造函数(道具){超级(道具);this.prompted = false;this.state = {令人耳目一新:假,appState: AppState.currentState,};}异步 componentDidMount() {AppState.addEventListener('change', this._handleAppStateChange);如果(!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 = 异步应用状态 =>{如果 (this.state.appState.match(/inactive|background/) &&appState === '活动'){cache.incrementAppViews();const doRefresh = await cache.shouldRefresh('feed');如果(doRefresh){this.props.fetchFeed();}}this.setState({appState});};_keyExtractor = ({实体}) =>(Entity.Key || Entity.Id || Entity.CampaignId || Entity.Code).toString();_gotoEvent = 事件 =>{cache.setRouteStarter('MainDrawer');this.props.setSelectedEvent(event);const title = `${event.LegislatureType} 事件`;this.props.navigation.navigate('EventDetails', {title});};_gotoSurveyBallot = 调查 =>{cache.setRouteStarter('MainDrawer');this.props.setSelectedSurvey(survey);this.props.navigation.navigate('SurveyDetails');};_gotoArticle = 文章 =>{cache.setRouteStarter('MainDrawer');this.props.setSelectedArticle(文章);this.props.navigation.navigate('ArticleDetails');};_onAlertActionButtonPress = 异步项目 =>{cache.setRouteStarter('MainDrawer');等待 this.props.setSelectedAlert(item.Entity);this.props.getCampaignDetails();如果(this.props.userEmailIsValidForVoterVoice){this.props.navigation.navigate('问卷');} 别的 {this.props.navigation.navigate('UnconfirmedEmail');}};_onSwipedOut = (swippedItem, index) =>{this.props.handleContentSwipe(this.props, {swippedItem, index});};_handleGetInvolved =(响应,实体)=>{如果(响应!== entity.IsSelected){const isTopic = entity.Category !== 'GetInvolved';常量项 = [{...实体,IsSelected:响应,},];如果(isTopic){this.props.handleUpdateTopics({topics: items});} 别的 {this.props.handleUpdateGetInvoved({involved: items});}}};renderItem = ({item, index}) =>{const {类型,实体} = 项目;如果(类型 === '事件'){返回 (<SwippableCard onSwipedOut={() =>this._onSwipedOut(item, index)}><EventFeedCard风格={styles.push}mainActionButtonPress={() =>this._gotoEvent(Entity)}事件={实体}/></SwippableCard>);}如果 (['SURVEY_SURVEY', 'SURVEY_BALLOT'].includes(Type)) {返回 (<SwippableCard onSwipedOut={() =>this._onSwipedOut(item, index)}><SurveyBallotFeedCard风格={styles.push}调查={实体}handleViewDetails={() =>this._gotoSurveyBallot(Entity)}/></SwippableCard>);}如果(类型 === 'SURVEY_MICRO'){返回 (<SwippableCard onSwipedOut={() =>this._onSwipedOut(item, index)}><MicroSurvey style={styles.push} selectedSurvey={Entity}/></SwippableCard>);}如果(类型 === '警报'){返回 (<SwippableCard onSwipedOut={() =>this._onSwipedOut(item, index)}><ActionAlertFeedCarddatePosted={Entity.StartDateUtc}风格={styles.push}标题={Entity.Headline}内容={Entity.Alert}mainActionButtonPress={() =>this._onAlertActionButtonPress(item)}secondaryActionButtonPress={() =>{this.props.setSelectedAlert(Entity);//eslint-disable-next-linethis.props.navigation.navigate("ActionAlertDetails", {内容:Entity.Alert,id: Entity.CampaignId,标题:Entity.Headline,});}}/></SwippableCard>);}如果(类型 === '文章'){返回 (<SwippableCard onSwipedOut={() =>this._onSwipedOut(item, index)}><文章FeedCard内容={实体}风格={styles.push}mainActionButtonPress={() =>this._gotoArticle(Entity)}/></SwippableCard>);}//更漂亮的忽略if (Type === 'NOTIFICATION' && Entity.Code === 'INDIVIDUAL_ADDRESS_HOME_MISSING') {返回 (<缺少地址卡风格={styles.push}导航={() =>this.props.navigation.navigate('HomeAddress')}/>);}if (['PREFERENCE_TOPIC', 'PREFERENCE_INVOLVEMENT'].includes(Type)) {返回 (<SwippableCard onSwipedOut={() =>this._onSwipedOut(item, index)}><GetInvolvedFeedCard风格={styles.push}标题={Entity.DisplayText}onPress={响应=>this._handleGetInvolved(响应,实体)}/></SwippableCard>);}返回空;};_onRefresh = async() =>{尝试 {this.setState({refreshing: true});this.props.fetchFeed().then(() => {this.setState({refreshing: false});}).catch(() => {this.setState({refreshing: false});});}赶上(e){this.setState({refreshing: false});}};_trackScroll = 异步事件 =>{尝试 {如果(this.prompted){返回;}const y = event.nativeEvent.contentOffset.y;const scrollHeight = 高度 * 0.8;const page = Math.round(Math.floor(y)/scrollHeight);const alert = await cache.shouldPromtpPushNotificationPermissions();const iOS = Platform.OS === 'ios';如果(警报&& iOS&&页面> 1){this.prompted = true;this._openPromptAlert();}}赶上(e){返回假;}};_openPromptAlert = () =>{警报.警报('推送通知访问',通过允许我们使用推送通知通知您,就您关心的问题和活动与 NFIB 保持联系",[{文本:'拒绝',onPress: () =>{cache.pushNotificationsPrompted();},风格:'取消',},{文本:'允许',onPress: () =>{OneSignal.registerForPushNotifications();cache.pushNotificationsPrompted();},},],{可取消:假});};_getAlertTitle = () =>{const {lastSwippedElement} = this.props;const {Type} = lastSwippedElement.swippedItem;if (Type.startsWith('PREFERENCE')) {返回偏好已取消";}开关(类型){案例事件":返回事件已取消";案例'SURVEY_BALLOT':return 'Ballot Dismissed';案例'SURVEY_SURVEY':return '调查被驳回';案例'SURVEY_MICRO':return 'Micro Survey Dismissed';案例文章":返回文章被驳回";案例警报":返回 '​​Action Alert Dismissed';默认:返回被解雇";}};使成为() {如果 (this.props.loading && !this.state.refreshing) {返回<加载中/>;}const contentStyles =this.props.feed.length >0 ?style.content : emptyStateStyles.container;返回 (<视图样式={styles.container}><平面列表contentContainerStyle={contentStyles}显示VerticalScrollIndicator={false}数据={this.props.feed}renderItem={this.renderItem}keyExtractor={this._keyExtractor}removeClippedSubviews={false}onRefresh={this._onRefresh}刷新={this.state.refreshing}ListEmptyComponent={() =>(<EmptyState navigation={this.props.navigation}/>)}scrollEventThrottle={100}onScroll={this._trackScroll}/>{this.props.displayAlert &&(<底部警报标题={this._getAlertTitle()}onPress={this.props.undoSwipeAction}hideAlert={this.props.hideUndoAlert}/>)}</查看>);}}const 样式 = StyleSheet.create({容器: {弹性:1,},内容: {填充水平:比例(8),paddingTop:比例(16),paddingBottom:比例(20),},推: {底边距:16,},});const mapState2Props = ({活动饲料,身份验证:{userEmailIsValidForVoterVoice},导航,}) =>{返回 {...活动饲料,userEmailIsValidForVoterVoice,加载:activityFeed.loading ||导航.deepLinkLoading,};};导出默认连接(mapState2Props,{获取饲料,获取活动详情,handleUpdateGetInvoved,处理更新主题,设置SelectedAlert,设置选定的文章,设置选择事件,setSelectedSurvey,处理内容滑动,撤销滑动操作,隐藏撤消警报,})(ActivityFeed);

我的代码没有看到任何明显的东西,我想知道 react-navigation 团队是否做了一些更改.

我正在使用 react-navigation 1.5.11 版和 react-native 0.60.4 版.

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

这个问题在我的应用程序中似乎很普遍.我也在这里得到错误:

这是 src/auth/screens/ResetLinkConfirmationAlert.js 文件:

从'react'导入React;从'react-redux'导入{connect};从'auth/components/ResetPasswordLinkConfirmationAlert'导入ResetPasswordLinkConfirmationAlert;从 'prop-types' 导入 PropTypes;const ResetLinkConfirmationAlert = ({resetEmail, navigation}) =>{const {params} = navigation.state;return <ResetPasswordLinkConfirmationAlert email={resetEmail} {...params}/>;};ResetLinkConfirmationAlert.propTypes = {导航:PropTypes.object,重置电子邮件:PropTypes.string,};const mapStateToProps = ({注册}) =>{const {resetEmail} = registrations.resetPasswordData;const email = resetEmail ||注册.验证电子邮件;返回 {resetEmail: email};};导出默认连接(mapStateToProps)(ResetLinkConfirmationAlert);

并将其导入到 src/navigation/auth/stack.js 中:

从react"导入React;从反应导航"导入 { StackNavigator, NavigationActions };import { Intro } from "auth/screens/Intro";从auth/screens/Login"导入{登录};从auth/screens/PasswordReset"导入{ PasswordReset };从auth/screens/RegisterNoEmail"导入 { RegisterNoEmail };从auth/screens/AskForMembership"导入 AskForMembership;从auth/screens/CompleteAccount"导入 { CompleteAccount };从auth/screens/ConfirmMemberAccount"导入 { ConfirmMemberAccount };从auth/screens/Register"导入{注册};从auth/screens/SetNewPassword"导入 SetNewPassword;从auth/screens/RegisterEmailPassword"导入 { RegisterEmailPassword };从auth/screens/ResetLinkConfirmationAlert"导入ResetLinkConfirmationAlert;从auth/screens/DetailsConfirmation"导入DetailsConfirmation;从auth/screens/AccountCreated"导入 AccountCreated;从导航组件/后退按钮"导入后退按钮;从导航组件/自定义标题"导入自定义标题;从导航组件/标题标题"导入标题标题;从主题"导入 { v2Colors };import { defaultStackConfig, defaultHeaderStyles } from "../config";const leftRegiterNavOptions = {title: "注册",headerStyle: defaultStackConfig.authHeaderStyle};const stack = StackNavigator({介绍: {屏幕:介绍,导航选项:{标头:空}},登记: {屏幕:注册,导航选项:({导航})=>({标题:<CustomHeader onPress={() =>navigation.goBack(null)}/>,headerStyle: defaultStackConfig.authHeaderStyle})},注册邮箱:{屏幕:RegisterNoEmail,导航选项:leftRegiterNavOptions},注册邮箱密码:{屏幕:注册电子邮件密码,导航选项:leftRegiterNavOptions},AskForMembership:{屏幕:AskForMembership,导航选项:{标题:<HeaderTitle/>,headerStyle: defaultStackConfig.authHeaderStyle}},确认会员帐号:{屏幕:ConfirmMemberAccount,导航选项:({导航})=>({标题:(<标题headerLeft={() =>(<BackButton onPress={() =>navigation.goBack(null)}/>)}/>),headerStyle: defaultStackConfig.authHeaderStyle})},完整帐户:{屏幕:完成帐户,导航选项:{标题:<HeaderTitle/>,headerStyle: defaultStackConfig.authHeaderStyle}},登录: {屏幕:登录,导航选项:({导航})=>({title: "登录",headerLeft: <BackButton onPress={() =>navigation.goBack(null)}/>,headerStyle: defaultStackConfig.authHeaderStyle})},重设密码: {屏幕:密码重置,导航选项:({导航})=>({title: "密码重置",headerLeft: <BackButton onPress={() =>navigation.goBack(null)}/>,headerStyle: defaultStackConfig.authHeaderStyle})},重置链接确认警报:{屏幕:ResetLinkConfirmationAlert,导航选项:({导航})=>({title: "密码重置",头左:(<返回按钮onPress={() =>{const resetNavAction = NavigationActions.reset({指数:0,键:空,动作:[NavigationActions.navigate({ routeName: "Intro" })]});navigation.dispatch(resetNavAction);}}/>),headerStyle: defaultStackConfig.authHeaderStyle})},

升级到 react-navigation 2.0.0 不是答案,因为我已经尝试过了,如果您打算建议升级到 3.x,请解释这将如何解决这个问题.>

有人建议,在 react-redux 7.1.0 的变更日志中,他们提到了一个注释,说明 PropTypes.func 必须更改为 PropTypes.elementType 如果元素正在作为道具传递

github.com/reduxjs/react-redux/releases/tag/v7.0.1

因此,如果我收到 SetNewPassword 的错误,我会像这样重构它:

export class CompleteAccount extends PureComponent {静态 propTypes = {加载:PropTypes.bool,newConfirmResetPassword: PropTypes.string,newResetPassword: PropTypes.string,重置用户密码:PropTypes.elementType.isRequired,setConfirnResetPassword: PropTypes.elementType.isRequired,setNewResetPassword: PropTypes.elementType.isRequired,验证错误:PropTypes.object};

然后在 navigation/auth/stack.js 中,我在 import 语句中添加了花括号,如下所示:

import { SetNewPassword } from "auth/screens/SetNewPassword";

但我仍然收到该错误消息,尽管我不确定我是否正确应用了该消息.同时我注意到 SetNewPassword.js 文件只有 CompleteAccount 的命名导出:

export class CompleteAccount extends PureComponent {静态 propTypes = {加载:PropTypes.bool,newConfirmResetPassword: PropTypes.string,newResetPassword: PropTypes.string,重置用户密码:PropTypes.elementType.isRequired,setConfirnResetPassword: PropTypes.elementType.isRequired,setNewResetPassword: PropTypes.elementType.isRequired,验证错误:PropTypes.object};......导出默认连接(mapStateToProps,{重置用户密码,设置新重置密码,设置确认重置密码})(完整账户);

不确定这个文件以前是如何以这种方式工作的.我通常将我的文件命名为与类或功能屏幕相同的名称,并以相同的名称导入.

进一步检查我发现有两个屏幕具有相同的类名函数.

CompleteAccount.js:

export class CompleteAccount extends PureComponent {静态 propTypes = {手机:PropTypes.string,手机更改:PropTypes.func.isRequired,城市:PropTypes.string,cityChanged: PropTypes.func.isRequired,错误:PropTypes.object,名字:PropTypes.string.isRequired,家庭地址:PropTypes.string,homeAddressChanged: PropTypes.func.isRequired,家庭电话:PropTypes.string,homePhoneChanged:PropTypes.func.isRequired,注册用户:PropTypes.object,registerUser: PropTypes.func.isRequired,状态:PropTypes.string,stateChanged: PropTypes.func.isRequired,邮政编码:PropTypes.string.isRequired,zipCodeChanged:PropTypes.func.isRequired,};

导出为:

export default connect(mapStateToProps, {城市变了,家庭地址已更改,家庭电话已更改,手机换了,状态改变,邮政编码已更改,注册用户,})(CompleteAccount);

然后是SetNewPassword.js:

也叫:

export class CompleteAccount extends PureComponent {静态 propTypes = {加载:PropTypes.bool,newConfirmResetPassword: PropTypes.string,newResetPassword: PropTypes.string,重置用户密码:PropTypes.func.isRequired,setConfirnResetPassword: PropTypes.func.isRequired,setNewResetPassword: PropTypes.func.isRequired,验证错误:PropTypes.object};.....导出默认连接(mapStateToProps,{重置用户密码,设置新重置密码,设置确认重置密码})(完整账户);

即使文件名完全不同.这很令人困惑,为什么他们不直接给第二个SetNewPassword 的类名?

解决方案

经过长达 6 天的艰苦努力,并尝试修复与我们对命名导出时使用花括号的理解背道而驰的问题后,我一直怀疑问题出在使用 react-navigation,因为我没有弄乱 react-navigation 版本或代码库.

问题在于 react-navigation 如何在 react-redux 版本 7 下工作或不工作.

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

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

I have looked through various similar posts here on SO regarding similar issue, but none of the answers solved it for me.

This is the full error:

So in my src/navigation/feed/stack.js its being defined like so:

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;

The actual component or screen is defined like so inside of 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);

I don't see anything apparent with my code and I am wondering if its some change that the react-navigation team did.

I am using react-navigation version 1.5.11 with react-native version 0.60.4.

Is this a compatibility issue with the RN version? Do I have no choice but to upgrade?

And this problem seems prevalent throughout my application. I also get the error here:

This is the src/auth/screens/ResetLinkConfirmationAlert.js file:

import React from 'react';
import {connect} from 'react-redux';
import ResetPasswordLinkConfirmationAlert from 'auth/components/ResetPasswordLinkConfirmationAlert';
import PropTypes from 'prop-types';

const ResetLinkConfirmationAlert = ({resetEmail, navigation}) => {
  const {params} = navigation.state;
  return <ResetPasswordLinkConfirmationAlert email={resetEmail} {...params} />;
};

ResetLinkConfirmationAlert.propTypes = {
  navigation: PropTypes.object,
  resetEmail: PropTypes.string,
};

const mapStateToProps = ({registrations}) => {
  const {resetEmail} = registrations.resetPasswordData;
  const email = resetEmail || registrations.verificationEmail;
  return {resetEmail: email};
};

export default connect(mapStateToProps)(ResetLinkConfirmationAlert);

and its being imported here in src/navigation/auth/stack.js:

import React from "react";
import { StackNavigator, NavigationActions } from "react-navigation";
import { Intro } from "auth/screens/Intro";
import { Login } from "auth/screens/Login";
import { PasswordReset } from "auth/screens/PasswordReset";
import { RegisterNoEmail } from "auth/screens/RegisterNoEmail";
import AskForMembership from "auth/screens/AskForMembership";
import { CompleteAccount } from "auth/screens/CompleteAccount";
import { ConfirmMemberAccount } from "auth/screens/ConfirmMemberAccount";
import { Register } from "auth/screens/Register";
import SetNewPassword from "auth/screens/SetNewPassword";
import { RegisterEmailPassword } from "auth/screens/RegisterEmailPassword";
import ResetLinkConfirmationAlert from "auth/screens/ResetLinkConfirmationAlert";
import DetailsConfirmation from "auth/screens/DetailsConfirmation";
import AccountCreated from "auth/screens/AccountCreated";

import BackButton from "navigation-components/BackButton";
import CustomHeader from "navigation-components/CustomHeader";
import HeaderTitle from "navigation-components/HeaderTitle";
import { v2Colors } from "theme";
import { defaultStackConfig, defaultHeaderStyles } from "../config";

const leftRegiterNavOptions = {
  title: "Register",
  headerStyle: defaultStackConfig.authHeaderStyle
};

const stack = StackNavigator(
  {
    Intro: {
      screen: Intro,
      navigationOptions: {
        header: null
      }
    },
    Register: {
      screen: Register,
      navigationOptions: ({ navigation }) => ({
        header: <CustomHeader onPress={() => navigation.goBack(null)} />,
        headerStyle: defaultStackConfig.authHeaderStyle
      })
    },
    RegisterNoEmail: {
      screen: RegisterNoEmail,
      navigationOptions: leftRegiterNavOptions
    },
    RegisterEmailPassword: {
      screen: RegisterEmailPassword,
      navigationOptions: leftRegiterNavOptions
    },
    AskForMembership: {
      screen: AskForMembership,
      navigationOptions: {
        header: <HeaderTitle />,
        headerStyle: defaultStackConfig.authHeaderStyle
      }
    },
    ConfirmMemberAccount: {
      screen: ConfirmMemberAccount,
      navigationOptions: ({ navigation }) => ({
        header: (
          <HeaderTitle
            headerLeft={() => (
              <BackButton onPress={() => navigation.goBack(null)} />
            )}
          />
        ),
        headerStyle: defaultStackConfig.authHeaderStyle
      })
    },
    CompleteAccount: {
      screen: CompleteAccount,
      navigationOptions: {
        header: <HeaderTitle />,
        headerStyle: defaultStackConfig.authHeaderStyle
      }
    },
    Login: {
      screen: Login,
      navigationOptions: ({ navigation }) => ({
        title: "Log In",
        headerLeft: <BackButton onPress={() => navigation.goBack(null)} />,
        headerStyle: defaultStackConfig.authHeaderStyle
      })
    },
    PasswordReset: {
      screen: PasswordReset,
      navigationOptions: ({ navigation }) => ({
        title: "Password Reset",
        headerLeft: <BackButton onPress={() => navigation.goBack(null)} />,
        headerStyle: defaultStackConfig.authHeaderStyle
      })
    },
    ResetLinkConfirmationAlert: {
      screen: ResetLinkConfirmationAlert,
      navigationOptions: ({ navigation }) => ({
        title: "Password Reset",
        headerLeft: (
          <BackButton
            onPress={() => {
              const resetNavAction = NavigationActions.reset({
                index: 0,
                key: null,
                actions: [NavigationActions.navigate({ routeName: "Intro" })]
              });
              navigation.dispatch(resetNavAction);
            }}
          />
        ),
        headerStyle: defaultStackConfig.authHeaderStyle
      })
    },

Upgrading to react-navigation 2.0.0 is not the answer because I already tried that and if you are going to suggest upgrading to 3.x please explain how that will solve this issue.

It was suggested that in the changelog for react-redux 7.1.0 they mention a note stating PropTypes.func has to be changed to PropTypes.elementType if an element is being passed as a prop

github.com/reduxjs/react-redux/releases/tag/v7.0.1

So in the case where I am getting the error for SetNewPassword, I refactored it like so:

export class CompleteAccount extends PureComponent {
  static propTypes = {
    loading: PropTypes.bool,
    newConfirmResetPassword: PropTypes.string,
    newResetPassword: PropTypes.string,
    resetUserPassword: PropTypes.elementType.isRequired,
    setConfirnResetPassword: PropTypes.elementType.isRequired,
    setNewResetPassword: PropTypes.elementType.isRequired,
    validationErrors: PropTypes.object
  };

and then in navigation/auth/stack.js I added curly braces to the import statement like so:

import { SetNewPassword } from "auth/screens/SetNewPassword";

but I am still getting that error message, although I am not sure if I applied that correctly. At the same time I have noticed that SetNewPassword.js file only has the named export of CompleteAccount:

export class CompleteAccount extends PureComponent {
  static propTypes = {
    loading: PropTypes.bool,
    newConfirmResetPassword: PropTypes.string,
    newResetPassword: PropTypes.string,
    resetUserPassword: PropTypes.elementType.isRequired,
    setConfirnResetPassword: PropTypes.elementType.isRequired,
    setNewResetPassword: PropTypes.elementType.isRequired,
    validationErrors: PropTypes.object
  };
.......

export default connect(
  mapStateToProps,
  {
    resetUserPassword,
    setNewResetPassword,
    setConfirnResetPassword
  }
)(CompleteAccount);

Not sure how this file was working before in that manner. I usually name my files the same name as the class or functional screen and import it with the same name.

On further inspection I see that there are two screens with the same class name function.

CompleteAccount.js:

export class CompleteAccount extends PureComponent {
  static propTypes = {
    cellPhone: PropTypes.string,
    cellPhoneChanged: PropTypes.func.isRequired,
    city: PropTypes.string,
    cityChanged: PropTypes.func.isRequired,
    errors: PropTypes.object,
    firstName: PropTypes.string.isRequired,
    homeAddress: PropTypes.string,
    homeAddressChanged: PropTypes.func.isRequired,
    homePhone: PropTypes.string,
    homePhoneChanged: PropTypes.func.isRequired,
    registeredUser: PropTypes.object,
    registerUser: PropTypes.func.isRequired,
    state: PropTypes.string,
    stateChanged: PropTypes.func.isRequired,
    zipCode: PropTypes.string.isRequired,
    zipCodeChanged: PropTypes.func.isRequired,
  };

which is exported as:

export default connect(mapStateToProps, {
  cityChanged,
  homeAddressChanged,
  homePhoneChanged,
  cellPhoneChanged,
  stateChanged,
  zipCodeChanged,
  registerUser,
})(CompleteAccount);

and then there is SetNewPassword.js:

which is also named:

export class CompleteAccount extends PureComponent {
  static propTypes = {
    loading: PropTypes.bool,
    newConfirmResetPassword: PropTypes.string,
    newResetPassword: PropTypes.string,
    resetUserPassword: PropTypes.func.isRequired,
    setConfirnResetPassword: PropTypes.func.isRequired,
    setNewResetPassword: PropTypes.func.isRequired,
    validationErrors: PropTypes.object
  };
.....

export default connect(
  mapStateToProps,
  {
    resetUserPassword,
    setNewResetPassword,
    setConfirnResetPassword
  }
)(CompleteAccount);

even though the file name is completely different. That is confusing, why didn't they just give the second one the class name of SetNewPassword?

解决方案

After a long grueling 6 days at this, and attempting fixes that went against our understanding of using curly braces when you got named exports, I always suspected that the problem was with react-navigation because I did not mess with the react-navigation version or the codebase.

The problem is how react-navigation works or does not work with react-redux version 7.

React Navigation does not recognize the object returned by React-Redux version 7.

The solution was to downgrade to React-Redux version 5.1.1.

这篇关于路由“ActivityFeed"的组件必须是 React 组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆