Skip to content

Commit 88f4c78

Browse files
committed
chore(Tab): use React.forwardRef() (#4265)
1 parent e7037ac commit 88f4c78

File tree

4 files changed

+48
-40
lines changed

4 files changed

+48
-40
lines changed

src/modules/Tab/Tab.js

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import PropTypes from 'prop-types'
33
import React from 'react'
44

55
import {
6-
ModernAutoControlledComponent as Component,
76
customPropTypes,
87
getElementType,
98
getUnhandledProps,
9+
useAutoControlledValue,
1010
} from '../../lib'
1111
import Grid from '../../collections/Grid/Grid'
1212
import GridColumn from '../../collections/Grid/GridColumn'
@@ -18,21 +18,25 @@ import TabPane from './TabPane'
1818
* @see Menu
1919
* @see Segment
2020
*/
21-
class Tab extends Component {
22-
getInitialAutoControlledState() {
23-
return { activeIndex: 0 }
21+
const Tab = React.forwardRef(function (props, ref) {
22+
const { grid, menu, panes, menuPosition, renderActiveOnly } = props
23+
24+
const [activeIndex, setActiveIndex] = useAutoControlledValue({
25+
state: props.activeIndex,
26+
defaultState: props.defaultActiveIndex,
27+
initialState: 0,
28+
})
29+
30+
const handleItemClick = (e, { index }) => {
31+
_.invoke(props, 'onTabChange', e, { ...props, activeIndex: index })
32+
setActiveIndex(index)
2433
}
2534

26-
handleItemClick = (e, { index }) => {
27-
_.invoke(this.props, 'onTabChange', e, { ...this.props, activeIndex: index })
28-
this.setState({ activeIndex: index })
29-
}
30-
31-
renderItems() {
32-
const { panes, renderActiveOnly } = this.props
33-
const { activeIndex } = this.state
35+
const renderItems = () => {
36+
if (renderActiveOnly) {
37+
return _.invoke(_.get(panes, `[${activeIndex}]`), 'render', props)
38+
}
3439

35-
if (renderActiveOnly) return _.invoke(_.get(panes, `[${activeIndex}]`), 'render', this.props)
3640
return _.map(panes, ({ pane }, index) =>
3741
TabPane.create(pane, {
3842
overrideProps: {
@@ -42,10 +46,7 @@ class Tab extends Component {
4246
)
4347
}
4448

45-
renderMenu() {
46-
const { menu, panes, menuPosition } = this.props
47-
const { activeIndex } = this.state
48-
49+
const renderMenu = () => {
4950
if (menu.tabular === true && menuPosition === 'right') {
5051
menu.tabular = 'right'
5152
}
@@ -54,55 +55,57 @@ class Tab extends Component {
5455
autoGenerateKey: false,
5556
overrideProps: {
5657
items: _.map(panes, 'menuItem'),
57-
onItemClick: this.handleItemClick,
58+
onItemClick: handleItemClick,
5859
activeIndex,
5960
},
6061
})
6162
}
6263

63-
renderVertical(menu) {
64-
const { grid, menuPosition } = this.props
64+
const renderVertical = (menuElement) => {
6565
const { paneWidth, tabWidth, ...gridProps } = grid
6666

67-
const position = menuPosition || (menu.props.tabular === 'right' && 'right') || 'left'
67+
const position = menuPosition || (menuElement.props.tabular === 'right' && 'right') || 'left'
6868

6969
return (
7070
<Grid {...gridProps}>
7171
{position === 'left' &&
72-
GridColumn.create({ width: tabWidth, children: menu }, { autoGenerateKey: false })}
72+
GridColumn.create({ width: tabWidth, children: menuElement }, { autoGenerateKey: false })}
7373
{GridColumn.create(
7474
{
7575
width: paneWidth,
76-
children: this.renderItems(),
76+
children: renderItems(),
7777
stretched: true,
7878
},
7979
{ autoGenerateKey: false },
8080
)}
8181
{position === 'right' &&
82-
GridColumn.create({ width: tabWidth, children: menu }, { autoGenerateKey: false })}
82+
GridColumn.create({ width: tabWidth, children: menuElement }, { autoGenerateKey: false })}
8383
</Grid>
8484
)
8585
}
8686

87-
render() {
88-
const menu = this.renderMenu()
89-
const rest = getUnhandledProps(Tab, this.props)
90-
const ElementType = getElementType(Tab, this.props)
91-
92-
if (menu.props.vertical) {
93-
return <ElementType {...rest}>{this.renderVertical(menu)}</ElementType>
94-
}
87+
const menuElement = renderMenu()
88+
const rest = getUnhandledProps(Tab, props)
89+
const ElementType = getElementType(Tab, props)
9590

91+
if (menuElement.props.vertical) {
9692
return (
97-
<ElementType {...rest}>
98-
{menu.props.attached !== 'bottom' && menu}
99-
{this.renderItems()}
100-
{menu.props.attached === 'bottom' && menu}
93+
<ElementType {...rest} ref={ref}>
94+
{renderVertical(menuElement)}
10195
</ElementType>
10296
)
10397
}
104-
}
10598

99+
return (
100+
<ElementType {...rest} ref={ref}>
101+
{menuElement.props.attached !== 'bottom' && menuElement}
102+
{renderItems()}
103+
{menuElement.props.attached === 'bottom' && menuElement}
104+
</ElementType>
105+
)
106+
})
107+
108+
Tab.displayName = 'Tab'
106109
Tab.propTypes = {
107110
/** An element type to render as (string or function). */
108111
as: PropTypes.elementType,

src/modules/Tab/TabPane.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,32 @@ import Segment from '../../elements/Segment/Segment'
1515
/**
1616
* A tab pane holds the content of a tab.
1717
*/
18-
function TabPane(props) {
18+
const TabPane = React.forwardRef(function (props, ref) {
1919
const { active, children, className, content, loading } = props
2020

2121
const classes = cx(useKeyOnly(active, 'active'), useKeyOnly(loading, 'loading'), 'tab', className)
2222
const rest = getUnhandledProps(TabPane, props)
2323
const ElementType = getElementType(TabPane, props)
2424

2525
const calculatedDefaultProps = {}
26+
2627
if (ElementType === Segment) {
2728
calculatedDefaultProps.attached = 'bottom'
2829
}
2930

3031
return (
31-
<ElementType {...calculatedDefaultProps} {...rest} className={classes}>
32+
<ElementType {...calculatedDefaultProps} {...rest} className={classes} ref={ref}>
3233
{childrenUtils.isNil(children) ? content : children}
3334
</ElementType>
3435
)
35-
}
36+
})
3637

3738
TabPane.defaultProps = {
3839
as: Segment,
3940
active: true,
4041
}
4142

43+
TabPane.displayName = 'TabPane'
4244
TabPane.propTypes = {
4345
/** An element type to render as (string or function). */
4446
as: PropTypes.elementType,

test/specs/modules/Tab/Tab-test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { sandbox } from 'test/utils'
77

88
describe('Tab', () => {
99
common.isConformant(Tab)
10+
common.forwardsRef(Tab)
11+
common.forwardsRef(Tab, { requiredProps: { menu: { vertical: true } } })
1012
common.hasSubcomponents(Tab, [TabPane])
1113

1214
const panes = [

test/specs/modules/Tab/TabPane-test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as common from 'test/specs/commonTests'
55

66
describe('TabPane', () => {
77
common.isConformant(TabPane)
8+
common.forwardsRef(TabPane)
89

910
common.implementsCreateMethod(TabPane)
1011

0 commit comments

Comments
 (0)