警告:React.createElement:类型无效-预期为字符串或类/函数,但得到:未定义

时间:2020-04-09 12:49:00

标签: javascript react-native jestjs enzyme

错误:

控制台。错误node_modules / react / cjs / react.development.js:172

警告:React.createElement:类型无效-预期为字符串(对于内置组件)或类/函数(对于复合组件),但得到:未定义。您可能忘记了从定义文件中导出组件,或者可能混淆了默认导入和命名导入。

console.log __tests __ / Dashboard-test.js:278

我为仪表板创建了一个自定义按钮,我正在尝试使用Jest和Enzyme测试其功能。根据我的阅读,此警告是由于进口混合使用而产生的,但我不认为这种情况适用于我的组件。

这是测试用例(可以通过,但会产生警告,如标题所示):

// __tests__/Dashboard-test.js
import React from 'react';
import {shallow, configure} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import {Provider} from 'react-redux';
import Dashboard from '../src/components/pages/Dashboard';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';

const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
configure({adapter: new Adapter()});
const props = {
  navigation: {
    navigate: jest.fn(),
  },
};

it('navigates to diary page when diary button is pressed', () => {
  const initialState = {
    authorisationReducer: {
      loggedIn: true,
    },
  };
  const store = mockStore(initialState);
  const wrapper = shallow(<Dashboard {...props} store={store} />).dive();
  const instance = wrapper.instance();

  instance.forceUpdate();

  const button = wrapper.findWhere(
    n => n.prop('accessibilityLabel') === 'Diary button',
  );

  button
    .props()
    .customClick();
  expect(props.navigation.navigate).toHaveBeenCalledWith('Diary');

  console.log(button.debug());
});

仪表板组件:

/* Dashboard with custom buttons to navigate between pages */
import React, {Component} from 'react';
import {View, StyleSheet} from 'react-native';
import firebase from 'react-native-firebase';
import SplashScreen from 'react-native-splash-screen';
import {DashboardButton} from '../layout/DashboardButton';
import {connect} from 'react-redux';
import Auth0 from 'react-native-auth0';
import base64 from 'react-native-base64';
import * as actions from '../../actions/index';
import {NavigationEvents} from 'react-navigation';

const auth0 = new Auth0({
  domain: 'xxx',
  clientId: 'xxx',
});

export class Dashboard extends Component {
  constructor(props) {
    super(props);

    // present log in page if user is not logged in
    if (props.loggedIn !== true) {
      this.login();
    }

    // show dashboard
    SplashScreen.hide();
  }

  login() {
    auth0.webAuth
      .authorize({scope: 'openid profile'})
      .then(credentials => {
        // successfully authenticated - set userId
        let userId = JSON.parse(
          base64
            .decode(this.unescape(credentials.idToken), 'base64')
            .toString(),
        ).sub;

        firebase
          .messaging()
          .getToken()
          .then(token => {
            this.props.addDevice(userId, token);
            this.props.loginUser(userId, token);
            this.props.loadInitialReminders();
            this.props.loadInitialDiaryEntries();
          });
      })
      .catch(error => {
        console.log(error);
      });
  }

  // converts base64 to base64url
  unescape(str) {
    // get the correct part of the token
    str = str.split('.')[1];
    return (str + '==='.slice((str.length + 3) % 4))
      .replace(/-/g, '+')
      .replace(/_/g, '/');
  }

  render() {
    return (
      <View accessible={true} style={styles.mainContainer}>
        <NavigationEvents
          onDidFocus={() => {
            if (this.props.loggedIn !== true) {
              this.login();
            }
          }}
        />

        <DashboardButton
          accessibilityLabel={'Physiotherapy button'}
          accessibilityHint={
            'Navigates to the Physiotherapy exercise categories screen'
          }
          disabled={!this.props.loggedIn}
          title="PHYSIOTHERAPY"
          customClick={() =>
            this.props.navigation.navigate('PhysiotherapyExerciseCategories')
          }
        />
        <DashboardButton
          accessibilityLabel={'Reminders button'}
          accessibilityHint={'Navigates to the Reminders screen'}
          disabled={!this.props.loggedIn}
          title="REMINDERS"
          customClick={() => this.props.navigation.navigate('Reminders')}
        />
        <DashboardButton
          accessibilityLabel={'Diary button'}
          accessibilityHint={'Navigates to the Diary screen'}
          disabled={!this.props.loggedIn}
          title="DIARY"
          customClick={() => this.props.navigation.navigate('Diary')}
        />
      </View>
    );
  }
}

const mapStateToProps = state => {
  return {
    loggedIn: state.authorisationReducer.loggedIn,
    reminders: state.remindersReducer.reminders,
    notificationsSet: state.remindersReducer.notificationsSet,
  };
};

export default connect(
  mapStateToProps,
  actions,
)(Dashboard);

const styles = StyleSheet.create({
  mainContainer: {
    flex: 1,
    backgroundColor: 'white',
    flexDirection: 'column',
  },
});

仪表板按钮:

/* Custom button on Dashboard */
import React from 'react';
import {TouchableOpacity, Text, StyleSheet} from 'react-native';
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome';
import {connect} from 'react-redux';
import * as actions from '../../actions/index';
import {
  faDumbbell,
  faBook,
  faCalendarCheck,
} from '@fortawesome/free-solid-svg-icons';
import {
  REGULAR_FONT,
  TULIP_DARK_MID_THEME_COLOUR,
  TULIP_LIGHT_MID_THEME_COLOUR,
  TULIP_LIGHT_THEME_COLOUR,
} from '../../constants';

const physiotherapyIcon = faDumbbell;
const diaryIcon = faBook;
const remindersIcon = faCalendarCheck;

export const DashboardButton = props => {
  let buttonStyle;
  let buttonIcon;

  if (props.title.toUpperCase() === 'PHYSIOTHERAPY') {
    buttonStyle = styles.physiotherapyButton;
    buttonIcon = physiotherapyIcon;
  } else if (props.title.toUpperCase() === 'DIARY') {
    buttonStyle = styles.diaryButton;
    buttonIcon = diaryIcon;
  } else if (props.title.toUpperCase() === 'REMINDERS') {
    buttonStyle = styles.remindersButton;
    buttonIcon = remindersIcon;
  }

  return (
    <TouchableOpacity
      accessibilityLabel={props.accessibilityLabel}
      accessibilityHint={props.accessibilityHint}
      disabled={props.disabled}
      style={buttonStyle}
      onPress={() => {
        if (props.enabledLongPress === false) {
          props.customClick();
        }
      }}
      onLongPress={() => {
        props.customClick();
      }}>
      <FontAwesomeIcon
        icon={buttonIcon}
        color={'white'}
        size={60}
        marginRight={25}
      />
      <Text style={styles.text}>{props.title}</Text>
    </TouchableOpacity>
  );
};

const mapStateToProps = state => {
  return {
    enabledLongPress: state.settingsReducer.enabledLongPress,
  };
};

export default connect(
  mapStateToProps,
  actions,
)(DashboardButton);

const styles = StyleSheet.create({
  physiotherapyButton: {
    backgroundColor: TULIP_DARK_MID_THEME_COLOUR,
    color: 'white',
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'flex-start',
    paddingLeft: 50,
  },
  remindersButton: {
    backgroundColor: TULIP_LIGHT_MID_THEME_COLOUR,
    color: 'white',
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'flex-start',
    paddingLeft: 50,
  },
  diaryButton: {
    backgroundColor: TULIP_LIGHT_THEME_COLOUR,
    color: 'white',
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'flex-start',
    paddingLeft: 50,
  },
  text: {
    color: 'white',
    fontFamily: REGULAR_FONT,
    fontSize: 25,
  },
});

我尝试将“仪表板”按钮更改为扩展Component的类,并将导入语句更改为包含{}(不是同时),但警告仍然存在。

1 个答案:

答案 0 :(得分:0)

我遇到了相同的错误消息。就我而言,问题是在“反应头盔”的警告中找到的。我已经对其进行了更新,现在需要将其作为命名组件导入

// import Helmet from "react-helmet"; old
import { Helmet } from "react-helmet"; 

您的Dashboard组件或其子组件之一可能在测试过程中失败,因为它需要一个道具来渲染该组件,但没有得到它。

我建议分解测试。首先为子组件编写测试。那应该告诉您需要传递哪些属性。

或者您可以注释掉一些子组件,直到可以通过测试。