我正在努力改变用打字稿编写的反应组件。我对代码库所做的更改涉及删除我们拥有的组件,以支持外部库react-dual-listbox。
一旦导入依赖项,我现有的所有测试都开始失败,我不确定原因。我收到的错误是
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();
});
});
});
组件:
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>
);
}
}
错误消息似乎很神秘。它似乎告诉我,它希望某些东西是一个字符串,但未定义。我不确定究竟是什么未定义的。但是,如果我只是不渲染DualListBox
,我可以通过测试,但这对我的问题没有帮助。
奇怪的是,它在浏览器中按预期工作,没有错误,问题只出在我的测试中。显然,拥有相同错误消息的人最终会成为导入问题,但如果它在浏览器中有效,则必须正确导入,不是吗?
我有什么遗漏或不理解的地方吗?