Skip to content

Instantly share code, notes, and snippets.

@alecmerdler
Last active September 1, 2017 07:37
Show Gist options
  • Save alecmerdler/1bbda1c26c05e7b64711e0ef2899c347 to your computer and use it in GitHub Desktop.
Save alecmerdler/1bbda1c26c05e7b64711e0ef2899c347 to your computer and use it in GitHub Desktop.
Async React Component in TypeScript
import * as React from 'react';
import { mount, ReactWrapper } from 'enzyme';
import { AsyncComponent } from '../../../public/components/utils/async';
describe('AsyncComponent', () => {
let wrapper: ReactWrapper;
const Foo = (props: {className: string}) => <div id={fooId} className={props.className} />;
const fooId = 'fooId';
const loadingBoxSelector = '.cos-status-box';
beforeEach(() => {
wrapper = null;
});
it('calls given loader function', (done) => {
const loader = () => new Promise<typeof Foo>((resolve) => {
resolve(Foo);
done();
});
wrapper = mount(<AsyncComponent loader={loader} />);
});
it('renders `LoadingBox` before `loader` promise resolves', (done) => {
const loader = () => new Promise<typeof Foo>(() => {
setTimeout(() => {
expect(wrapper.find(loadingBoxSelector).exists()).toBe(true);
done();
}, 10);
});
wrapper = mount(<AsyncComponent loader={loader} />);
});
it('continues to display `LoadingBox` if `loader` promise is rejected', (done) => {
const loader = () => new Promise<typeof Foo>((_, reject) => {
reject('epic fail');
setTimeout(() => {
expect(wrapper.find(loadingBoxSelector).exists()).toBe(true);
done();
}, 10);
});
wrapper = mount(<AsyncComponent loader={loader} />);
});
it('renders component resolved from `loader` promise', (done) => {
const loader = () => new Promise<typeof Foo>((resolve) => {
resolve(Foo);
setTimeout(() => {
expect(wrapper.find(`#${fooId}`).exists()).toBe(true);
done();
}, 10);
});
wrapper = mount(<AsyncComponent loader={loader} />);
});
it('passes given props to rendered component', (done) => {
const className = 'col-md-1';
const loader = () => new Promise<typeof Foo>((resolve) => {
resolve(Foo);
setTimeout(() => {
expect(wrapper.find(`#${fooId}`).props().className).toEqual(className);
done();
}, 10);
});
wrapper = mount(<AsyncComponent loader={loader} className={className} />);
});
});
import * as React from 'react';
import { LoadingBox } from './status-box';
export class AsyncComponent extends React.Component<AsyncComponentProps, AsyncComponentState> {
constructor(props) {
super(props);
this.state = {Component: null};
}
componentDidMount() {
this.props.loader().then((Component) => {
this.setState({Component});
});
}
render() {
const {Component} = this.state;
return Component != null
? <Component {...this.props} />
: <LoadingBox />;
}
}
export type AsyncComponentProps = {loader: () => Promise<React.ComponentClass | React.StatelessComponent>};
export type AsyncComponentState = {Component: React.ComponentClass | React.StatelessComponent};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment