React Component未设置HTML属性

时间:2018-07-12 15:05:18

标签: javascript html reactjs

我遇到了一个奇怪的问题,我在使用React之前从未遇到过。

我正在创建一个Button组件,该组件需要与旧版Button组件向后兼容。这要求我在实现过程中做一些不幸的事情...

旧版Button的问题之一是传递tabIndex时有错字。 tabIndex的旧Button属性为tapIndex

使用类似...的地图应该很容易映射

<Button tabIndex={this.props.tabIndex || this.props.tapIndex || 0}></Button>

但是由于某种原因,情况并非如此...

如果子级的tabIndex属性映射到this.props.tapIndex,则呈现的元素将没有设置tabIndex。简而言之:

这有效

<Button
  ...
  tabIndex={this.props.tabIndex}
  ...
>Hello</Button>

这不是

<Button
  ...
  tabIndex={this.props.tapIndex}
  ...
>Hello</Button>

我已经调试了组件的执行,并且this.props.tapIndex的设置确实正确且等于预期值。但同样,在这种情况下,呈现元素上不存在tabIndex属性。

Button.js

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import {
  stylesToStyled,
  cleanClassNames,
  extractProps
} from '@ns/utils';

import getKind, {
  KINDS,
  KIND_DICT,
  PRESENTATION_DICT,
  BOOLEAN_PROPS
} from './utils';
import PrimaryButton from './components/PrimaryButton';
import SecondaryButton from './components/SecondaryButton';
import TertiaryButton from './components/TertiaryButton';


const TYPES = ['button', 'submit', 'reset'];
const COMPONENT_DICT = {
  [KIND_DICT.PRIMARY]: PrimaryButton,
  [KIND_DICT.SECONDARY]: SecondaryButton,
  [KIND_DICT.TERTIARY]: TertiaryButton,
};

class Button extends Component {
  componentDidMount() {
    this.setAnalyticsMessageOnDOM();
  }

  componentDidUpdate() {
    this.setAnalyticsMessageOnDOM();
  }

  get StyledButton() {
    // Get the appropriate Component
    const kind = getKind(this.props, extractProps(
      this.props.className,
      BOOLEAN_PROPS
    ));

    // Add passed in styles
    const styles = stylesToStyled(this.props.style);
    return styled(COMPONENT_DICT[kind])(
      styles.properties,
      ...styles.values
    );
  }

  get tabIndex() {
    if (Number.isInteger(this.props.tabIndex)) {
      return this.props.tabIndex;
    }

    if (Number.isInteger(this.props.tapIndex)) {
      return this.props.tapIndex;
    }

    return 0;
  }

  setAnalyticsMessageOnDOM() {
    // Terrible Hack
    this.buttonRef.setAttribute(
      'analyticstrack',
      this.props.analyticstrackmessage
    );
  }

  render () {
    const {
      className,
      children,
      ...props
    } = this.props;
    const { StyledButton } = this;

    return (
      <StyledButton
        className={cleanClassNames(className, BOOLEAN_PROPS)}
        tabIndex={this.tabIndex}
        innerRef={el => {this.buttonRef = el}}
        {...props}
      >
        {children}
      </StyledButton>
    );
  }
}

Button.propTypes = {
  [KIND_DICT.PRIMARY]: PropTypes.bool,
  [KIND_DICT.SECONDARY]: PropTypes.bool,
  [KIND_DICT.TERTIARY]: PropTypes.bool,
  [PRESENTATION_DICT.DISABLED]: PropTypes.bool,
  [PRESENTATION_DICT.INVERTED]: PropTypes.bool,
  children: PropTypes.node.isRequired,
  className: PropTypes.string,
  style: PropTypes.object,
  onClick: PropTypes.func,
  tabIndex: PropTypes.number,
  // Deprectation Warning: Typo that is being used in production, use tabindex.
  tapIndex: PropTypes.number,
  type: PropTypes.oneOf(TYPES),
  // Deprectation Warning: Use tabindex.
  kind: PropTypes.oneOf(KINDS),
  analyticstrackmessage: PropTypes.string,
};

Button.defaultProps = {
  [KIND_DICT.PRIMARY]: false,
  [KIND_DICT.SECONDARY]: false,
  [KIND_DICT.TERTIARY]: false,
  [PRESENTATION_DICT.DISABLED]: false,
  [PRESENTATION_DICT.INVERTED]: false,
  className: undefined,
  style: {},
  onClick: () => {},
  tabIndex: undefined,
  tapIndex: undefined,
  type: TYPES[0],
  kind: undefined,
  analyticstrackmessage: undefined,
};

export { TYPES };
export default Button;

如果任何人对造成此问题的原因有任何想法,请随时发表评论或回答。一切都会有所帮助。

谢谢。

2 个答案:

答案 0 :(得分:1)

object创建一个tabIndex,然后使用Button将其分配给spread operator

const tabIndex = {tabIndex:this.props.tabIndex || this.props.tapIndex }

按钮

<Button {...tabIndex}
  ...

  ...
>Hello</Button>

答案 1 :(得分:0)

尝试@RIYAJ KHAN的建议后,我发现了问题。

我从:

  render () {
    const {
      className,
      children,
      ...props
    } = this.props;
    const { StyledButton } = this;

    return (
      <StyledButton
        className={cleanClassNames(className, BOOLEAN_PROPS)}
        tabIndex={this.tabIndex}
        innerRef={el => {this.buttonRef = el}}
        {...props}
      >
        {children}
      </StyledButton>
    );
  }

收件人:

  render () {
    const {
      className,
      children,
      tabIndex,
      tapIndex,
      ...props
    } = this.props;
    const { StyledButton } = this;
    const tabIdx = { tabIndex: tapIndex || tabIndex || 0 };

    return (
      <StyledButton
        className={cleanClassNames(className, BOOLEAN_PROPS)}
        innerRef={el => {this.buttonRef = el}}
        {...tabIdx}
        {...props}
      >
        {children}
      </StyledButton>
    );
  }
}

然后慢慢地回到折断的渲染方法。

我注意到的是,在最新示例中,tapIndex剩余表达式中排除了tabIndex...props,该表达式在我的StyledButton之后添加到tabIndex={...}

我确定的是,当不排除这些属性时,它们最终将以某种方式覆盖我要添加的tabIndex。因此,传递给React.createElement的是:

React.createElement(
  StyledButton,
  { ..., tabIndex: undefined, tapIndex: N },
  children
)

不是我在返回的组件上设置的tabIndex

我的最终解决方案是-仅渲染方法:

  render () {
    const {
      className,
      children,
      tabIndex,
      tapIndex,
      ...props
    } = this.props;
    const { StyledButton } = this;

    return (
      <StyledButton
        className={cleanClassNames(className, BOOLEAN_PROPS)}
        tabIndex={tabIndex || tapIndex || 0}
        innerRef={el => {this.buttonRef = el}}
        {...props}
      >
        {children}
      </StyledButton>
    );
  }