我在React中使用Sample tabs组件,如下所示:
import React from 'react';
import ReactDOM from 'react-dom';
import Tabs from 'xxx/components/tabs';
import Tab from 'xxx/components/tab';
import TabHeader from 'xxx/components/tabheader';
import TabBody from 'xxx/components/tabbody';
class Sam extends React.Component{
constructor(props){
super(props);
this.state = {
count: 0
};
this.chngState = this.chngState.bind(this);
}
chngState(ev){
console.log("ev.target.value: ",ev.target.value);
this.setState({count: parseInt(ev.target.value)});
}
render() {
return (<div>
<input type="number" onChange={(ev)=>{this.chngState(ev)}}/>
<div className="reactComp">
<Tabs>
<Tab active>
<TabHeader >Auto Attendant</TabHeader>
<TabBody className="pad-left-1r">
<h4>Testing Button {this.state.count}</h4>
<p>
This is just to demonstarate the use of tabs.
You could also make a `ul` inside like this:
</p>
<ul>
<li>First</li>
<li>Second</li>
<li>Third</li>
<li>Fourth</li>
</ul>
</TabBody>
</Tab>
<Tab>
<TabHeader>Call Park</TabHeader>
<TabBody className="pad-left-1r">
<h4>Testing Button 2</h4>
<p>
This is just to demonstarate the use of tabs.
You could also make a `ul` inside like this:
</p>
<ul>
<li>Fifth</li>
<li>Sixth</li>
<li>Seventh</li>
<li>Eighth</li>
</ul>
</TabBody>
</Tab>
</Tabs>
</div>
</div>);
}
}
ReactDOM.render(<Sam />, document.querySelector("#full_component"));
哪个渲染组件如下:
但是当我在输入中进行更改时,它并没有反映在孩子身上(我认为因为我正在使用React.cloneElement来应对孩子)
所以在我给定的场景我如何获取我的要求?
import React from 'react';
import PropTypes from 'prop-types';
/* eslint-disable react/prefer-stateless-function */
class Tab extends React.Component {
}
Tab.propTypes = {
/**
* Controls whether the current tab is active or inactive.
* @ignore
*/
active: PropTypes.bool,
/**
* Callback for activating tab
*/
onActivation: PropTypes.func,
/**
* Makes the tab closable
*/
closable: PropTypes.bool
};
Tab.defaultProps = {
active: false,
onActivation: null,
closable: false
};
export default Tab;
import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import tabStyles from './tab_body.css';
export default class TabBody extends React.Component {
constructor (props) {
super(props);
this.state = {
active: true
};
}
getTabBodyClass () {
const {className} = this.props;
let constructedClass = cx(tabStyles.tabBody, {
[`${tabStyles.invisible}`]: !this.props.isActive
}, className);
return constructedClass;
}
render () {
let tabClass = this.getTabBodyClass();
return (<div className={tabClass} >
{this.props.children}
</div>);
}
}
TabBody.propTypes = {
className: PropTypes.string,
/**
* children component
*/
children: PropTypes.node,
/**
* Configuration holds the state of the tab
* @ignore
* @private
*/
isActive: PropTypes.bool
};
TabBody.defaultProps = {
className: '',
children: null,
isActive: false
};
import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import tabHeaderStyles from './tab_header.css';
export default class TabHeader extends React.Component {
constructor (props) {
super(props);
this.state = {
isActive: true
};
this.onHeaderClick = this.onHeaderClick.bind(this);
this.onHeaderKeydown = this.onHeaderKeydown.bind(this);
this.onTabClose = this.onTabClose.bind(this);
this.onCloseButtonKeydown = this.onCloseButtonKeydown.bind(this);
}
onHeaderClick (e) {
let handled = false;
e.stopPropagation();
if (typeof this.props.onActivationUser === 'function') {
handled = this.props.onActivationUser(e.target);
}
if (handled) {
e.preventDefault();
} else {
this.props.onActivation(e.target);
}
}
onHeaderKeydown (e) {
if (e.keyCode === 13 || e.keyCode === 32) {
let handled = false;
e.stopPropagation();
if (typeof this.props.onActivationUser === 'function') {
handled = this.props.onActivationUser(e.target);
}
if (handled) {
e.preventDefault();
} else {
this.props.onActivation(e.target);
}
}
}
onTabClose (e) {
e.stopPropagation();
let handled = false;
if (typeof this.props.onCloseUser === 'function') {
handled = this.props.onCloseUser(e.target);
}
if (handled) {
e.preventDefault();
} else {
this.props.onClose(e.target);
}
}
onCloseButtonKeydown (e) {
e.stopPropagation();
if (e.keyCode === 13) {
let handled = false;
e.stopPropagation();
if (typeof this.props.onCloseUser === 'function') {
handled = this.props.onCloseUser(e.target);
}
if (handled) {
e.preventDefault();
} else {
this.props.onClose(e.target);
}
}
}
getTabHeaderClass (receivedClass) {
const {isActive, size, type, align, orientation} = this.props;
let className = cx(`${tabHeaderStyles.tabHeader} ft-sz-14`, {
[`${tabHeaderStyles.verticalOrientation}`]: orientation === 'vertical',
[`${tabHeaderStyles.tabsHeader}`]: type === 'tabs',
[`${tabHeaderStyles.pillsHeader}`]: type === 'pills' && orientation !== 'vertical',
[`${tabHeaderStyles.pillsHeaderVertical}`]: type === 'pills' && orientation === 'vertical',
[`${tabHeaderStyles.grayHeader}`]: type === 'gray' && orientation !== 'vertical',
[`${tabHeaderStyles.grayHeaderVertical}`]: type === 'gray' && orientation === 'vertical',
[`${tabHeaderStyles.headerActiveTabs}`]: type === 'tabs' && isActive,
[`${tabHeaderStyles.headerActivePills}`]: type === 'pills' && isActive,
[`${tabHeaderStyles.headerActiveGray}`]: type === 'gray' && isActive,
[`${tabHeaderStyles.smallHeader}`]: size === 'small',
[`${tabHeaderStyles.largeHeader}`]: size === 'large',
[`${tabHeaderStyles.alignJustified}`]: align === 'justified'
});
return cx(className, receivedClass);
}
render () {
const { className, closable, size, tabIndex, onActivation, onClose, onCloseUser,
onActivationUser, align, isActive, uuid, ...otherProps } = this.props;
let tabClass = this.getTabHeaderClass(className);
return (
<button
className={tabClass}
onMouseDown={this.onHeaderClick}
onKeyDown={this.onHeaderKeydown}
data-name={this.props.uuid}
{...otherProps}
>
{this.props.children}
{closable &&
<a
className={tabHeaderStyles.closeContainer}
data-name={this.props.uuid}
onMouseDown={this.onTabClose}
onKeyDown={this.onCloseButtonKeydown}
href="javascript:;"
>
✕
</a>
}
</button>
);
}
}
TabHeader.propTypes = {
/**
* Alignment of the titles
* @ignore
*/
align: PropTypes.oneOf(['start', 'justified']),
/**
* HTML Markup to be placed inside the header
* @ignore
*/
children: PropTypes.node,
/**
* Custom class for the Header
*/
className: PropTypes.string,
/**
* If tab is closable
*/
closable: PropTypes.bool,
/**
* State of active tab
* @ignore
* @private
*/
isActive: PropTypes.bool,
/**
* On Activation callback from tabs
* @ignore
*/
onActivation: PropTypes.func,
/**
* On Activation callback from tab
* @ignore
*/
onActivationUser: PropTypes.func,
/**
* On Close callback from tabs
* @ignore
*/
onClose: PropTypes.func,
/**
* On Close callback from tab
* @ignore
*/
onCloseUser: PropTypes.func,
/**
* Size of the tabs component
* @ignore
* @private
*/
size: PropTypes.oneOf(['small', 'regular', 'large']),
/**
* Orientaion of the menu items
* @ignore
* @private
*/
orientation: PropTypes.oneOf(['vertical', 'horizontal']),
/**
* Index of the tab
* @ignore
* @private
*/
tabIndex: PropTypes.number,
/**
* Type of the header
* @ignore
*/
type: PropTypes.oneOf(['tabs', 'pills', 'gray']),
/**
* Unique identifier for tab header
* @ignore
*/
uuid: PropTypes.string
};
TabHeader.defaultProps = {
align: 'start',
children: null,
className: '',
closable: false,
orientation: 'horizontal',
isActive: false,
onActivation: null,
onActivationUser: null,
onClose: null,
onCloseUser: null,
size: 'regular',
tabIndex: 0,
type: 'tabs',
uuid: null
};
import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import Helpers from '../../utils/helpers';
import Tab from '../tab';
import TabHeader from '../tabheader';
import TabBody from '../tabbody';
import Toast from '../toast';
import tabsStyles from './tabs.css';
/**
* Tabs are a great way to allow the user to switch between
* several pages that are full screen.
*/
export default class Tabs extends React.Component {
constructor (props) {
super(props);
this.state = {
currentTab: 0,
compUpdateCalled:0,
compMountCalled:0,
};
this.titleList = [];
this.bodyList = [];
this.curentTabUUID = null;
this.setActiveTab = this.setActiveTab.bind(this);
this.tabClickHandler = this.tabClickHandler.bind(this);
this.tabCloseHandler = this.tabCloseHandler.bind(this);
this.updateChildElm = this.updateChildElm.bind(this);
this.activateTab = this.activateTab.bind(this);
}
updateChildElm () {
const {align, children, orientation, type, size} = this.props;
let uuid = 0;
if (children && children.length > 0) {
children.forEach((child, idx) => {
if (child.type === Tab) {
uuid = Helpers.getComponentId('Tab');
if (idx === 0 || child.props.active === true) {
this.curentTabUUID = uuid;
}
let grandChildren = child.props.children;
grandChildren.forEach((grandChild) => {
if (grandChild.type === TabHeader) {
grandChild = React.cloneElement(grandChild, {
closable: child.props.closable,
isActive: uuid === this.curentTabUUID,
key: idx.toString(),
onActivation: this.tabClickHandler,
onActivationUser: child.props.onActivation,
onClose: this.tabCloseHandler,
onCloseUser: child.props.onClose,
orientation: orientation,
align: align,
type: type,
size: size,
uuid: uuid
});
this.titleList.push(grandChild);
}
if (grandChild.type === TabBody) {
grandChild = React.cloneElement(grandChild, {
uuid: uuid,
isActive: uuid === this.curentTabUUID,
key: idx.toString()
});
this.bodyList.push(grandChild);
}
});
}
});
}
this.setActiveTab(this.curentTabUUID);
}
componentWillMount () {
console.log("compMountCalled: ",this.state.compUpdateCalled);
this.updateChildElm();
}
componentWillReceiveProps () {
console.log("compUpdateCalled: ",this.state.compUpdateCalled);
this.updateChildElm();
}
setActiveTab (uuid) {
this.setState({
currentTab: uuid
});
}
getTabsClass (recievedClass) {
const {orientation} = this.props;
let augmentedClass = cx(tabsStyles.tabsContainer, {
[`${tabsStyles.verticalOrientation}`]: orientation === 'vertical'
});
return cx(augmentedClass, recievedClass);
}
getTabHeaderClass () {
const orientation = this.props.orientation;
let augmentedClass = cx(tabsStyles.tabHeaderContainer, {
[`${tabsStyles.verticalOrientationHeader} flex-col`]: orientation === 'vertical'
});
return augmentedClass;
}
getTabBodyClass () {
const orientation = this.props.orientation;
let augmentedClass = cx(tabsStyles.tabBodyContainer, 'flex1', {
[`${tabsStyles.verticalOrientationBody}`]: orientation === 'vertical'
});
return augmentedClass;
}
activateTab (list) {
return list.map((item) => (
React.cloneElement(item, {
isActive: item.props.uuid === this.state.currentTab
}))
);
}
tabClickHandler (e) {
if (typeof this.props.onTabChange === 'function') {
this.props.onTabChange(this, e.target);
}
this.setActiveTab(e.getAttribute('data-name'));
}
tabCloseHandler (e) {
if (typeof this.props.onTabChange === 'function') {
this.props.onTabChange(this, e.target);
}
if (this.titleList.length === 1) {
const toastWithTitle = (
<Toast
key={Date.now()}
type="danger"
>
Cannot close the last remaining tab of the tabset.
</Toast>);
window.collab.notify(toastWithTitle);
} else {
let closeTabIndex = this.titleList.findIndex((item) => (item.props.uuid === e.getAttribute('data-name')));
if (this.titleList[closeTabIndex].props.uuid === this.curentTabUUID) {
let nextSelect = this.titleList[closeTabIndex - 1];
if (!nextSelect) {
nextSelect = this.titleList[closeTabIndex + 1];
}
this.curentTabUUID = nextSelect.props.uuid;
}
this.titleList.splice(closeTabIndex, 1);
this.bodyList.splice(closeTabIndex, 1);
}
this.setActiveTab(this.curentTabUUID);
}
render () {
const {align, children, className, onTabChange,
orientation, type, size, ...otherProps} = this.props;
let tabsClass = this.getTabsClass(className);
let tabHeaderClass = this.getTabHeaderClass();
let tabBodyClass = this.getTabBodyClass();
return (
<div className={tabsClass} {...otherProps}>
<div className={tabHeaderClass}>
{this.activateTab(this.titleList)}
</div>
<div className={tabBodyClass}>
{this.activateTab(this.bodyList)}
</div>
</div>
);
}
}
Tabs.propTypes = {
/**
* Alignment od the header
*/
align: PropTypes.oneOf(['start', 'justified']),
/**
* HTML Markup to be placed inside the header
* @ignore
*/
children: PropTypes.node,
/**
* Custom class for the Header
*/
className: PropTypes.string,
/**
* Orientation configuration
*/
orientation: PropTypes.oneOf(['vertical', 'horizontal']),
/**
* Callback to be called whenever a tab-change takes place
* with currentTab as first parameter and nextTab as the second parameter.
*/
onTabChange: PropTypes.func,
/**
* Size of the tabs component
*/
size: PropTypes.oneOf(['small', 'regular', 'large']),
/**
* Type of the header
*/
type: PropTypes.oneOf(['tabs', 'pills', 'gray'])
};
Tabs.defaultProps = {
align: 'start',
className: '',
children: null,
orientation: 'horizontal',
onTabChange: null,
size: 'regular',
type: 'tabs'
};