Предупреждение: React.createElement: тип недействителен - ожидал строку или класс / функцию, но получил: undefined - PullRequest
0 голосов
/ 09 апреля 2020

Ошибка:

console.error node_modules / реагировать / cjs / реаги.девелопмент. js: 172

Предупреждение: React.createElement : тип недействителен - ожидал строку (для встроенных компонентов) или класс / функцию (для составных компонентов), но получил: undefined. Скорее всего, вы забыли экспортировать компонент из файла, в котором он определен, или вы перепутали импорт по умолчанию и имена.

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,
  },
});

Я попытался изменить кнопку Dashboard на класс, расширяющий Component, и изменить операторы импорта, чтобы включить {} (не одновременно), но предупреждение не исчезло.

1 Ответ

0 голосов
/ 17 апреля 2020

Я столкнулся с тем же сообщением об ошибке. В моем случае проблема была найдена в предупреждении от "ответного шлема". Я обновил его, и теперь мне нужно вместо него импортировать его как именованный компонент

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

Ваш компонент Dashboard или один из его подкомпонентов, вероятно, дает сбой во время теста, поскольку для его визуализации требуется подпорка, но не получить это.

Предлагаю разбить тесты. Сначала напишите тесты для подкомпонентов. Это должно сказать вам, какие свойства вам нужно передать.

Или вы можете закомментировать некоторые подкомпоненты, пока не сможете пройти тест.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...