Javascript 使用外部组件库时的不变冲突

Javascript 使用外部组件库时的不变冲突,javascript,reactjs,typescript,jestjs,enzyme,Javascript,Reactjs,Typescript,Jestjs,Enzyme,我正在对用typescript编写的react组件进行更改。我对代码库所做的更改包括删除我们拥有的一个组件,以支持外部库 一旦导入依赖项,我现有的所有测试都开始失败,我不太清楚原因。我收到的错误是 Error: Uncaught [Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite componen

我正在对用typescript编写的react组件进行更改。我对代码库所做的更改包括删除我们拥有的一个组件,以支持外部库

一旦导入依赖项,我现有的所有测试都开始失败,我不太清楚原因。我收到的错误是

Error: Uncaught [Invariant Violation: Element type is invalid: expected a string 
(for built-in components) or a class/function (for composite components) but got: 
undefined. You likely forgot to export your component from the file it's defined in, 
or you might have mixed up default and named imports.

Check the render method of `default_1`.
这是我的测试文件:

import { shallow, configure, mount } from 'enzyme';
import * as React from 'react';
import PolicyEditor, { PolicyEditorProps } from '../PolicyEditor';
import * as Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

const defaultProps: PolicyEditorProps = {
  id: 'id',
  serviceId: 'service id',
  data: { data: { policy: { id: '1', name: 'policy', permissions: [] } } },
  permissionsData: { data: { service: { id: '1', permissions: [] } } },
  create: jest.fn(),
  update: jest.fn(),
  updatePermissions: jest.fn(),
  delete: jest.fn(),
  back: jest.fn(),
};

describe('PolicyEditor.tsx', () => {
  it('sets the state when the name is changed', done => {
    const wrapper = mount(<PolicyEditor {...defaultProps} />);
    const input = wrapper.find('input[id="policyName"]').first();

    const event = { target: { value: 'new policy name' } };

    input.simulate('change', event);

    process.nextTick(() => {
      expect(wrapper.state().name).toEqual('new policy name');
      done();
    });
  });
});
从'enzyme'导入{shall,configure,mount};
从“React”导入*作为React;
从“../PolicyEditor”导入PolicyEditor,{PolicyEditorProps};
从'enzyme-Adapter-react-16'导入*作为适配器;
配置({adapter:newadapter()});
常量defaultProps:PolicyEditorProps={
id:'id',
serviceId:“服务id”,
数据:{data:{policy:{id:'1',名称:'policy',权限:[]},
permissionsData:{data:{service:{id:'1',权限:[]},
create:jest.fn(),
更新:jest.fn(),
updatePermissions:jest.fn(),
删除:jest.fn(),
back:jest.fn(),
};
描述('PolicyEditor.tsx',()=>{
它('设置名称更改时的状态',完成=>{
const wrapper=mount();
const input=wrapper.find('input[id=“policyName”]”)。first();
const event={target:{value:'new policy name'};
输入。模拟(“变更”,事件);
process.nextTick(()=>{
expect(wrapper.state().name).toEqual('newpolicy name');
完成();
});
});
});
组成部分:

import 'react-dual-listbox/lib/react-dual-listbox.css';

import * as React from 'react';
import { MutationFn } from 'react-apollo';
import { isQueryReady, isQueryLoading } from '../util/actionHelpers';
import {
  Permission,
  PolicyWithPermissions,
  ServicePermissionsQueryResult,
  PolicyQueryResult,
} from './types';
import AuthModal from '../shared/AuthModal';
import { Container, Row, Col, Label, Button, Form, FormGroup, Input } from 'reactstrap';
import DualListBox from 'react-dual-listbox';

export interface PolicyEditorProps {
  id?: string;
  serviceId: string;
  data: PolicyQueryResult;
  permissionsData: ServicePermissionsQueryResult;
  create: MutationFn;
  update: MutationFn;
  updatePermissions: MutationFn;
  delete: MutationFn;
  back(): void;
}

interface PolicyEditorState {
  name: string;
  selectedPermissions: Permission[];
}

export default class extends React.Component<PolicyEditorProps, PolicyEditorState> {
  static getDerivedStateFromProps(
    nextProps: PolicyEditorProps,
    prevState: PolicyEditorState
  ): Partial<PolicyEditorState> | null {
    if (nextProps.id && isQueryReady(nextProps.data)) {
      const policy: PolicyWithPermissions = nextProps.data.data!.policy;

      return {
        name: policy.name,
        selectedPermissions: policy.permissions,
      };
    }

    if (!nextProps.id) {
      return {
        name: '',
      };
    }

    return null;
  }

  constructor(props: PolicyEditorProps) {
    super(props);
    this.state = {
      name: '',
      selectedPermissions: [],
    };
  }

  render() {
    const isEdit: boolean = !!this.props.id;
    if (isEdit) {
      if (isQueryLoading(this.props.data)) {
        return <div>Loading...</div>;
      }
      if (this.props.data.error !== undefined) {
        return <div>Something went wrong!</div>;
      }
    }

    const { loading, error } = this.props.permissionsData;
    return (
      <AuthModal title="Policy Editor" isOpen={true} toggle={this.props.back}>
        <Form>
          <FormGroup>
            <Label for="policyName">Name</Label>
            <Input
              id="policyName"
              onChange={e => this.setState({ name: e.target.value })}
              value={this.state.name}
            />
          </FormGroup>
          {isEdit ? (
            <div>
              <hr />
              {loading ? (
                <div>Loading...</div>
              ) : error ? (
                <div>Error</div>
              ) : (
                <Container className="m-2">
                  <Row>
                    <Col className="text-center" xs="6">
                      Available Permissions
                    </Col>
                    <Col className="text-center" xs="6">
                      Current Permissions
                    </Col>
                  </Row>
                  <DualListBox
                    canFilter={true}
                    options={this.props.permissionsData.data!.service.permissions.map(p => ({
                      value: p.id,
                      label: p.name,
                    }))}
                    selected={this.state.selectedPermissions.map(p => p.id)}
                    onChange={this.updateSelectedPermissions}
                  />
                </Container>
              )}
              <FormGroup>
                <Row>
                  <Col xs={{ size: 1 }}>
                    <Button onClick={this.onUpdate} color="success">
                      Update
                    </Button>
                  </Col>
                  <Col xs={{ size: 1, offset: 1 }}>
                    <Button onClick={this.onDelete} color="danger">
                      Delete
                    </Button>
                  </Col>
                </Row>
              </FormGroup>
            </div>
          ) : (
            <FormGroup>
              <Button color="success" onClick={this.onCreate}>
                Create
              </Button>
            </FormGroup>
          )}
        </Form>
      </AuthModal>
    );
  }
}
import'react dual listbox/lib/react dual listbox.css';
从“React”导入*作为React;
从'react apollo'导入{MutationFn};
从“../util/actionHelpers”导入{isQueryReady,isQueryLoading};
进口{
许可,
具有权限的策略,
ServicePermissionsQueryResult,
PolicyQueryResult,
}来自“./类型”;
从“../shared/AuthModal”导入AuthModal;
从“reactstrap”导入{容器、行、列、标签、按钮、窗体、窗体组、输入};
从“react dual listbox”导入DualListBox;
导出接口PolicyEditorProps{
id?:字符串;
serviceId:字符串;
数据:PolicyQueryResult;
permissionsData:ServicePermissionsQueryResult;
创建:MutationFn;
更新:突变fn;
更新权限:MutationFn;
删除:突变fn;
back():无效;
}
接口策略编辑器状态{
名称:字符串;
selectedPermissions:权限[];
}
导出默认类扩展React.Component{
静态getDerivedStateFromProps(
下一步:PolicyEditorProps,
prevState:PolicyEditorState
):部分|空{
if(nextrops.id&&isQueryReady(nextrops.data)){
常量策略:PolicyWithPermissions=nextrops.data.data!.policy;
返回{
name:policy.name,
selectedPermissions:policy.permissions,
};
}
如果(!nextrops.id){
返回{
名称:“”,
};
}
返回null;
}
构造函数(道具:PolicyEditorProps){
超级(道具);
此.state={
名称:“”,
所选权限:[],
};
}
render(){
const isEdit:boolean=!!this.props.id;
如果(isEdit){
if(isQueryLoading(this.props.data)){
返回装载。。。;
}
if(this.props.data.error!==未定义){
返回出错的地方!;
}
}
const{loading,error}=this.props.permissionsData;
返回(
名称
this.setState({name:e.target.value})}
值={this.state.name}
/>
{iEdit(

{加载( 加载。。。 ):错误( 错误 ) : ( 可用权限 当前权限 ({ 值:p.id, 标签:p.name, }))} selected={this.state.selectedPermissions.map(p=>p.id)} onChange={this.updateSelectedPermissions} /> )} 更新 删除 ) : ( 创造 )} ); } }
错误消息似乎很神秘。它似乎告诉我,它期望某个东西是字符串,但没有定义。我不太确定到底什么是未定义的。但是,如果我只是不呈现
DualListBox
,我就能够通过测试,但这对我的问题没有帮助

奇怪的是,它在浏览器中按预期工作,没有任何错误,问题只在于我的测试。显然,有相同错误消息的人最终会导致导入问题,但如果它在浏览器中工作,则必须正确导入,不是吗

这里有什么我遗漏或不理解的吗