将流类型的React组件转换为JSX-React

问题描述 投票:0回答:1

我在React中需要一个可访问的工具提示组件,并且遇到了React Tooltip。它是用流编写的,但是我在构建中使用了jsx。我想将语法转换为jsx,但在render方法中遇到了障碍。请就我需要更改的内容提出建议。

流程工具提示:https://pastebin.com/EfQQBKZ0

// @flow

import React, { Component } from 'react';
import type { ElementProps, ComponentType } from 'react';

export type LabelProps = {
    labelAttributes: {
        tabIndex: '0',
        'aria-describedby': string,
        onFocus: () => void,
    },
    isHidden: boolean,
};

export type OverlayProps = {
    overlayAttributes: {
        role: 'tooltip',
        tabIndex: '-1',
        id: string,
        'aria-hidden': string,
    },
    isHidden: boolean,
};

export type TooltipState = {
    isFocused: boolean,
    isHovered: boolean,
};

export type TooltipProps = ElementProps<'div'> & {
    label: ComponentType<LabelProps>,
    overlay: ComponentType<OverlayProps>,
};

let counter = 0;

class Tooltip extends Component<TooltipProps, TooltipState> {
    constructor(props: TooltipProps) {
        super(props);
        this.identifier = `react-accessible-tooltip-${counter}`;
        counter += 1;
    }

    state = {
        isFocused: false,
        isHovered: false,
    };

    componentDidMount() {
        document.addEventListener('keydown', this.handleKeyDown);
        document.addEventListener('touchstart', this.handleTouch);
    }

    componentWillUnmount() {
        document.removeEventListener('keydown', this.handleKeyDown);
        document.removeEventListener('touchstart', this.handleTouch);
    }

    onFocus = () => {
        this.setState({ isFocused: true });
    };

    onBlur = ({
        relatedTarget,
        currentTarget,
    }: SyntheticFocusEvent<HTMLElement>) => {
        // relatedTarget is better for React testability etc, but activeElement works as an IE11 fallback:
        const newTarget = relatedTarget || document.activeElement;

        // The idea of this logic is that we should only close the tooltip if focus has shifted from the tooltip AND all of its descendents.
        if (!(newTarget && newTarget instanceof HTMLElement)) {
            this.setState({ isFocused: false });
        } else if (!currentTarget.contains(newTarget)) {
            this.setState({ isFocused: false });
        }
    };

    onMouseEnter = () => {
        this.setState({ isHovered: true });
    };

    onMouseLeave = () => {
        this.setState({ isHovered: false });
    };

    // This handles the support for touch devices that do not trigger blur on 'touch-away'.
    handleTouch = ({ target }: Event) => {
        const { activeElement } = document;

        if (
            activeElement instanceof Element &&
            target instanceof Element &&
            this.container instanceof Element &&
            !this.container.contains(target) && // touch target not a tooltip descendent
            this.state.isFocused // prevent redundant state change
        ) {
            this.setState({ isFocused: false });
            activeElement.blur();
        } else if (
            activeElement instanceof Element &&
            target instanceof Element &&
            this.container instanceof Element &&
            this.container.contains(target) && // touch target is on tooltip descendant
            !this.state.isFocused // prevent redundant state change
        ) {
            this.setState({ isFocused: true });
        }
    };

    handleKeyDown = ({ key, keyCode, which }: KeyboardEvent) => {
        if (key === 'Escape' || keyCode === 27 || which === 27) {
            this.setState({ isFocused: false });
        }
    };

    container: ?HTMLDivElement;
    identifier: string;

    render() {
        const { label: Label, overlay: Overlay, ...rest } = this.props;

        const { isFocused, isHovered } = this.state;
        const isHidden = !(isFocused || isHovered);

        const labelProps: LabelProps = {
            labelAttributes: {
                tabIndex: '0',
                'aria-describedby': this.identifier,
                onFocus: this.onFocus,
            },
            isHidden,
        };

        const overlayProps: OverlayProps = {
            overlayAttributes: {
                role: 'tooltip',
                tabIndex: '-1',
                id: this.identifier,
                'aria-hidden': isHidden.toString(),
            },
            isHidden,
        };

        return (
            <div
                {...rest}
                onBlur={this.onBlur}
                ref={ref => {
                    this.container = ref;
                }}
                onMouseEnter={this.onMouseEnter}
                onMouseLeave={this.onMouseLeave}
            >
                <Label {...labelProps} />
                <Overlay {...overlayProps} />
            </div>
        );
    }
}

export default Tooltip;

JSX工具提示:https://pastebin.com/QhQqRw24

import React, { Component } from 'react';
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import InfoIcon from '../../InfoIcon/InfoIcon';

let counter = 0;

class TooltipContainer extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isFocused: false,
      isHovered: false,
    };
    this.identifier = `react-accessible-tooltip-${counter}`;
    counter += 1;
    this.onFocus = this.onFocus.bind(this);
    this.onBlur = this.onBlur.bind(this);
    this.onMouseEnter = this.onMouseEnter.bind(this);
    this.onMouseLeave = this.onMouseLeave.bind(this);
    this.handleTouch = this.handleTouch.bind(this);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.container = React.createRef();
  }

  componentDidMount() {
    document.addEventListener('keydown', this.handleKeyDown);
    document.addEventListener('touchstart', this.handleTouch);
  }

  componentWillUnmount() {
    document.removeEventListener('keydown', this.handleKeyDown);
    document.removeEventListener('touchstart', this.handleTouch);
  }

  onFocus() {
    this.setState({ isFocused: true });
  };

  onBlur({
    relatedTarget,
    currentTarget,
  }){
    // relatedTarget is better for React testability etc, but activeElement works as an IE11 fallback:
    const newTarget = relatedTarget || document.activeElement;

    // The idea of this logic is that we should only close the tooltip if focus has shifted from the tooltip AND all of its descendents.
    if (!(newTarget)) {
      this.setState({ isFocused: false });
    } else if (!currentTarget.contains(newTarget)) {
      this.setState({ isFocused: false });
    }
  }

  onMouseEnter() {
    this.setState({ isHovered: true });
  }

  onMouseLeave() {
    this.setState({ isHovered: false });
  }

  // This handles the support for touch devices that do not trigger blur on 'touch-away'.
  handleTouch({ target }) {
    const { activeElement } = document;

    if (
      activeElement instanceof Element &&
      target instanceof Element &&
      this.container instanceof Element &&
      !this.container.contains(target) && // touch target not a tooltip descendent
      this.state.isFocused // prevent redundant state change
    ) {
      this.setState({ isFocused: false });
      activeElement.blur();
    } else if (
      activeElement instanceof Element &&
      target instanceof Element &&
      this.container instanceof Element &&
      this.container.contains(target) && // touch target is on tooltip descendant
      !this.state.isFocused // prevent redundant state change
    ) {
      this.setState({ isFocused: true });
    }
  }

  handleKeyDown({ key, keyCode, which }) {
    if (key === 'Escape' || keyCode === 27 || which === 27) {
      this.setState({ isFocused: false });
    }
  }


  render() {
    console.log('props', this.props)
    const { label: Label, overlay: Overlay, ...rest } = this.props;

    const { isFocused, isHovered } = this.state;
    const isHidden = !(isFocused || isHovered);

    const labelProps = {
      labelAttributes: {
        tabIndex: '0',
        'aria-describedby': this.identifier,
        onFocus: this.onFocus,
      },
      isHidden,
    };

    const overlayProps = {
      overlayAttributes: {
        role: 'tooltip',
        tabIndex: '-1',
        id: this.identifier,
        'aria-hidden': isHidden.toString(),
      },
      isHidden,
    };

    return (
      <div
        {...rest}
        onBlur={this.onBlur}
        ref={ref => {
          this.container = ref;
        }}
        onMouseEnter={this.onMouseEnter}
        onMouseLeave={this.onMouseLeave}
      >
        <a><InfoIcon icon={faInfoCircle} {...labelProps} /></a>
        <Overlay {...overlayProps} />
      </div>
    );
  }
}

export default TooltipContainer;
javascript reactjs jsx flowtype
1个回答
1
投票

我无法理解,为什么您不想只安装该软件包并按照API说明使用它。

关于您的问题,Flow只是一个类型检查器,如果要摆脱它,只需从代码中删除与流相关的内容。

[我看到您做了一些与流程变化无关的事情,例如裁判。在带有流的示例中,有一个回调引用,但是在没有示例的示例中,您尝试使用新的refs API,但仍将其用作回调引用。乍一看,还有更多其他东西...

加号

渲染中的包版广告

听起来不太有用,您遇到了什么问题。

© www.soinside.com 2019 - 2024. All rights reserved.