HTMLElement上的HOC组件

时间:2017-06-02 14:14:22

标签: reactjs typescript

我写了这个令人讨厌的野兽:

export type DelayHoverElementProps<TElement extends HTMLElement> = React.HTMLProps<TElement> 
& {
    hoverDelay: number,
    onHoverChanged: (isHovering: boolean) => void
};

export function DelayHoverElementFactory<
    K extends keyof HTMLElementTagNameMap, 
    E extends HTMLElementTagNameMap[K], 
    C extends React.ComponentClass<DelayHoverElementProps<E>>
>(tagName: K): C {
    return class extends React.Component<
        DelayHoverElementProps<E>, 
        { isHovering: boolean }
    >{
        constructor() {
            super();
            this.state = { isHovering: true };
        }

        private _delayTimer: NodeJS.Timer;

        private onMouseOver = (e: React.MouseEvent<E>) => {
            const { hoverDelay, onHoverChanged } = this.props;
            clearTimeout(this._delayTimer);
            this.setState({ isHovering: true }, () => {
                this._delayTimer = setTimeout(() => {
                    onHoverChanged(true);
                }, hoverDelay);
            });
        };

        private onMouseOut = (e: React.MouseEvent<E>) => {
            const { hoverDelay, onHoverChanged } = this.props;
            clearTimeout(this._delayTimer);
            this.setState({ isHovering: false }, () => {
                this._delayTimer = setTimeout(() => {
                    onHoverChanged(false);
                }, hoverDelay);
            });
        };

        render() {
            const { hoverDelay, onHoverChanged, ...props } = this.props;
            const tagProps: React.HTMLProps<E> = {
                ...props,
                onMouseOver: this.onMouseOver,
                onMouseOut: this.onMouseOut
            };

            return React.createElement(tagName, tagProps);
        }
    } as any; // <---
}
这个测试用法:(摩卡,柴,sinon,酶;测试将通过)

const props: DelayHoverElementProps<HTMLDivElement> = {
    hoverDelay: 200,
    onHoverChanged: index => { },
    className: 'test',
    children: 'content'
};
const spy = sinon.spy(props, 'onHoverChanged');
const expected = 
    <div className="test" onMouseOver={()=>{}} onMouseOut={()=>{}}>content</div>;

const Div = DelayHoverElementFactory('div');
const actual = shallow(<Div {...props as any} />) // <---
// equalJsx is custom asserting method, with will convert JSX to string and compare that ...
expect(actual).to.equalJsx(expected);

actual.find('div').simulate('mouseOver');
expect(spy).to.be.not.called;
await new Promise(resolve => setTimeout(resolve, props.hoverDelay))
expect(spy).to.be.calledOnce;
expect(spy).to.be.calledWith(true);

我删除了这些as any然后我收到了这些错误:

// first
Type 'typeof (Anonymous class)' is not assignable to type 'C'.

// second
Type '{ defaultChecked?: boolean; defaultValue?: string | string[]; suppressContentEditableWarning?: bo...' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<DelayHoverElementProps<HTMLDivElement>, ...'.
  Type '{ defaultChecked?: boolean; defaultValue?: string | string[]; suppressContentEditableWarning?: bo...' is not assignable to type 'IntrinsicClassAttributes<Component<DelayHoverElementProps<HTMLDivElement>, ComponentState>>'.
    Types of property 'ref' are incompatible.
      Type 'Ref<HTMLDivElement>' is not assignable to type 'Ref<Component<DelayHoverElementProps<HTMLDivElement>, ComponentState>>'.
        Type '(instance: HTMLDivElement) => any' is not assignable to type 'Ref<Component<DelayHoverElementProps<HTMLDivElement>, ComponentState>>'.
          Type '(instance: HTMLDivElement) => any' is not assignable to type '(instance: Component<DelayHoverElementProps<HTMLDivElement>, ComponentState>) => any'.
            Types of parameters 'instance' and 'instance' are incompatible.
              Type 'Component<DelayHoverElementProps<HTMLDivElement>, ComponentState>' is not assignable to type 'HTMLDivElement'.
                Property 'align' is missing in type 'Component<DelayHoverElementProps<HTMLDivElement>, ComponentState>'.

所以我在这里张贴这个并希望,有些打字大师会知道如何解决它...(as any正在运作,但我不喜欢它 - 感觉不对)

1 个答案:

答案 0 :(得分:0)

经过一番摆弄后,我最终得到了这段代码:

import * as React from 'react';

export type DelayHoverElementProps<TElement extends HTMLElement> =
    React.HTMLProps<TElement> & {
        hoverDelay: number,
        onHoverChanged: (isHovering: boolean) => void
    };

export function DelayHoverElementFactory<
    K extends keyof HTMLElementTagNameMap, 
    E extends HTMLElementTagNameMap[K]
>(tagName: K): React.StatelessComponent<DelayHoverElementProps<E>> {
    return function (props: DelayHoverElementProps<E>) {
        const { hoverDelay, onHoverChanged, ...rest } = props;

        let _delayTimer: NodeJS.Timer;

        const onMouseOver = (e: React.MouseEvent<E>) => {
            clearTimeout(_delayTimer);
            _delayTimer = setTimeout(() => {
                onHoverChanged(true);
            }, hoverDelay);
        };

        const onMouseOut = (e: React.MouseEvent<E>) => {
            clearTimeout(_delayTimer);
            _delayTimer = setTimeout(() => {
                onHoverChanged(false);
            }, hoverDelay);
        };

        const tagProps: React.HTMLProps<E> = { ...rest, onMouseOver, onMouseOut };
        return React.createElement(tagName, tagProps);
    };
}

不再依赖于施放到any