我遇到了一个奇怪的问题,我在使用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;
如果任何人对造成此问题的原因有任何想法,请随时发表评论或回答。一切都会有所帮助。
谢谢。
答案 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>
);
}