【问题标题】:Warning: React.createElement: type is invalid -- expected a string or a class/function but got: undefined警告:React.createElement:类型无效——需要一个字符串或一个类/函数,但得到:未定义
【发布时间】:2020-04-09 12:49:00
【问题描述】:

错误:

console.error 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 的类,并将导入语句更改为包含 {}(不是同时),但警告仍然存在。

【问题讨论】:

  • 这行代码中有一个单引号,这会产生一个语法错误:&lt;DashboardButton accessibilityLabel={'Diary button} ... /&gt; 这是一个语法错误。对 js 模板使用反引号:` ` 或删除它!检查错误是否仍然出现并保持不变...回来并在此处报告。
  • 这只是问题中的一个错字,我的错误。我现在已经修好了。
  • 您能否提供更多您收到的错误消息?也许有一个堆栈跟踪?您是否在第二个列表中导入了{Component} from "react" ?否则你也可以从 React.Component 扩展...如果你发布整个文件会更好,这样我们就不需要猜测你做了什么。
  • 嗨,我再次更新了问题以显示完整的错误、仪表板和仪表板按钮文件。我希望这有助于澄清事情
  • 为什么仪表板按钮功能中有两个导出尝试删除第一个导出,即 export const DashboardButton to const DashboardButton

标签: javascript react-native jestjs enzyme


【解决方案1】:

我遇到了同样的错误信息。就我而言,问题是在“react-helmet”的警告中发现的。我已经更新了它,现在需要将它作为命名组件导入

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

您的仪表板组件或其子组件之一可能在测试期间失败,因为它需要一个道具来呈现该组件但没有得到它。

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

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

【讨论】:

    猜你喜欢
    • 2022-12-12
    • 1970-01-01
    • 2017-08-30
    • 2018-01-18
    • 2017-08-06
    • 2021-11-17
    • 2020-01-20
    • 1970-01-01
    • 2021-12-03
    相关资源
    最近更新 更多