我有一个如下所示的输入组件:
import React from 'react';
import { PropTypes } from 'prop-types';
const NewCartonInput =
({
value,
itemId,
onChange,
}) => (
<input
id={itemId}
onChange={onChange}
type="number"
value={value}
/>
);
NewCartonInput.propTypes = {
value: PropTypes.number.isRequired,
itemId: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
};
export default NewCartonInput;
它位于此父组件中:
import React from 'react';
import { PropTypes } from 'prop-types';
import { sumBy, isEmpty, isEqual, toNumber } from 'lodash';
import T from 'i18n-react';
import uuidv1 from 'uuid/v1';
import { formatNumber } from 'utils/currencyAndNumberFormatting';
import Container from 'components/Container';
import Content from 'components/Content';
import Header from 'components/Header';
import SearchBox from 'components/SearchBox';
import ListWithHeader from 'components/List/ListWithHeader';
import ListItemWithNumber from 'components/List/ListItemWithNumber';
import Footer from 'components/Footer';
import FooterText from 'components/Footer/Text';
import Prompt from 'components/Prompt';
import TransferPickList from 'components/PrintTemplates/TransferPickList';
import Print from 'components/Print';
import { formatDate } from 'utils/date';
import NewCartonInput from './NewCartonInput';
import { showProgressBar } from './utils';
import './NewTransfer.scss';
export default class NewCarton extends React.Component {
constructor(props) {
super(props);
this.onEditClicked = this.onEditClicked.bind(this);
this.onClickPrint = this.onClickPrint.bind(this);
this.onCloseCartonClicked = this.onCloseCartonClicked.bind(this);
this.handleChange = this.handleChange.bind(this);
this.listCartons = this.listCartons.bind(this);
this.state = {
editMode: false,
showPrintDialog: false,
disableSearch: false,
};
}
componentWillMount() {
const {
onGetCartonItems,
onClearNewCarton,
transferRequestId,
hasChanges,
routerAction,
} = this.props;
if (routerAction === 'PUSH') {
onClearNewCarton();
}
if ((!hasChanges || routerAction === 'PUSH') && transferRequestId) {
onGetCartonItems(transferRequestId);
}
}
shouldComponentUpdate(newProps) {
const newItems = newProps.items;
const oldItems = this.props.items;
return !isEqual(newItems, oldItems);
}
componentWillUnmount() {
const { onClearShipment } = this.props;
onClearShipment();
}
onEditClicked() {
this.forceUpdate();
this.setState({
editMode: !this.state.editMode,
});
this.forceUpdate();
}
onClickPrint() {
this.forceUpdate();
this.setState({
showPrintDialog: !this.state.showPrintDialog,
});
}
onCloseCartonClicked(cartonContext) {
const { onClickClose } = this.props;
this.setState({
disableSearch: true,
});
onClickClose(cartonContext);
}
handleChange(e) {
const { items, onUpdateQuantity } = this.props;
const updateItem = items.filter((item) =>
item.itemId === e.target.id,
);
const itemQuantity = toNumber(e.target.value);
updateItem.total += itemQuantity;
onUpdateQuantity(e.target.id, itemQuantity);
}
listCartons() {
const {
items,
onItemDelete,
transferRequestId,
} = this.props;
const list =
items.filter((item) => !item.deleted || item.deleted === false);
return list.map(({
itemId,
itemDescription,
packed,
total,
}, index, filteredItems) => {
let onDeletePress;
if (deleteMode(filteredItems, this.state.editMode)) {
onDeletePress = (e) => {
e.stopPropagation();
onItemDelete(itemId);
};
}
const packedAll = packed === total && total > 0;
let countDetailsStr;
let countDetailsTotal;
if (transferRequestId) {
countDetailsStr = (
<li styleName="newCartonOf">
of
</li>
);
countDetailsTotal = (
<li styleName="newCartonTotal">
{total}
</li>
);
}
return (
<li styleName="newCartonQuantity" key={uuidv1()}>
<ul styleName="newCartonDetails">
<ListItemWithNumber
header={itemId}
section={itemDescription}
key={itemId}
onPress={() => {}}
onDeletePress={onDeletePress}
checkIcon={packedAll}
icon={false}
/>
<ul styleName="newCartonQtyDetails">
<li styleName="newCartonQtyInput">
<NewCartonInput
value={packed}
itemId={itemId}
onChange={this.handleChange}
/>
</li>
{countDetailsStr}
{countDetailsTotal}
</ul>
</ul>
</li>
);
});
}
render() {
const {
onSearch,
items,
destinationId,
shipmentId,
sourceId,
onClickSave,
onClickClose,
shouldNotBlock,
loading,
transferRequestId,
destId,
hasChanges,
printRequestedItems,
currentLocale,
disableSearch,
} = this.props;
const canEditItems = !transferRequestId && !isEmpty(items);
const backTitle = destId ? T.translate('views.transfers.title.transfers')
: T.translate('views.transfers.title.transferCartons');
const itemsPacked = sumBy(
items, item => item.packed,
);
const totalItems = sumBy(
items, item => item.total,
);
const destinationHeader = {
label: T.translate('views.transfers.labels.destination'),
value: destinationId,
};
let itemsHeader;
if (transferRequestId) {
itemsHeader = {
label: T.translate('views.transfers.labels.items'),
value: `${formatNumber(itemsPacked)}/${formatNumber(totalItems)}`,
};
} else {
itemsHeader = {
label: T.translate('views.transfers.labels.items'),
value: `${formatNumber(itemsPacked)}`,
};
}
const cartonContext = {
shipmentId,
transferRequestId,
sourceId,
destinationId,
items: items.filter((item) => !item.deleted || item.deleted === false),
itemsPacked,
totalItems,
};
let shouldDisableSave = loading || !hasChanges || isEmpty(items);
let headerAction = () => onClickSave(cartonContext);
let headerTitle = T.translate('views.transfers.buttons.save');
const printbtn = T.translate('views.transfers.buttons.print');
const editbtn = T.translate('views.transfers.buttons.edit');
const closeCartonbtn = T.translate('views.transfers.buttons.closeCarton');
let footer;
if (this.state.editMode) {
headerAction = this.onEditClicked;
headerTitle = T.translate('views.transfers.buttons.done');
shouldDisableSave = loading;
footer = (
<FooterText textAlign="center">
{T.translate('views.transfers.text.instructions')}
</FooterText>
);
} else {
footer = (
<Footer
links={{
[`${editbtn}`]: {
onClick: this.onEditClicked,
hidden: status === 'PACKED',
},
[`${printbtn}`]: {
onClick: () => this.onClickPrint(),
disabled: loading,
hidden: !printRequestedItems,
},
[`${closeCartonbtn}`]: {
onClick: () => onClickClose(cartonContext),
disabled: shouldDisableSave,
},
}}
/>
);
}
let printDialog;
const PickTemplate = (<TransferPickList
printingDate={formatDate(new Date(), currentLocale)}
destinationId={destinationId}
items={cartonContext.items}
/>);
if (this.state.showPrintDialog) {
printDialog = (
<div styleName="printDialog">
<Print
templateRef="TransferPickList"
templates={[PickTemplate]}
nativePrint
onClose={this.onClickPrint}
/>
</div>
);
} else {
printDialog = null;
}
return (
<Container>
<Header
title={T.translate('views.transfers.title.newCarton')}
backTitle={backTitle}
rightLink={{
action: headerAction,
title: headerTitle,
disabled: shouldDisableSave,
}}
/>
<Content>
{showProgressBar(loading)}
<Prompt
onlyOnBack
shouldNotBlock={shouldNotBlock}
confirmNavigation={T.translate(
'views.common.messages.confirmNavigation')}
/>
<SearchBox
onSearch={!disableSearch ? onSearch : () => {}}
placeholder={
T.translate('shared.components.searchBox.placeholderItemNumber')
}
searchButtonText={
T.translate('shared.components.searchBox.searchButtonText')
}
inputDisabled={disableSearch}
/>
<ListWithHeader
headerLeft={destinationHeader}
headerRight={itemsHeader}
>
{this.listCartons()}
</ListWithHeader>
{printDialog}
</Content>
{footer}
</Container>
);
}
}
function deleteMode(filteredItems, editMode) {
return editMode
&& filteredItems
&& filteredItems.length > 0;
}
NewCarton.propTypes = {
onClearShipment: PropTypes.func.isRequired,
onItemDelete: PropTypes.func,
onSearch: PropTypes.func.isRequired,
items: PropTypes.arrayOf(PropTypes.shape({
itemId: PropTypes.string.isRequired,
itemDescription: PropTypes.string.isRequired,
packed: PropTypes.number.isRequired,
total: PropTypes.number.isRequired,
})),
onGetCartonItems: PropTypes.func.isRequired,
onClearNewCarton: PropTypes.func.isRequired,
hasChanges: PropTypes.bool.isRequired,
routerAction: PropTypes.string.isRequired,
transferRequestId: PropTypes.string,
destId: PropTypes.string,
sourceId: PropTypes.string.isRequired,
destinationId: PropTypes.string.isRequired,
shipmentId: PropTypes.string,
onClickSave: PropTypes.func.isRequired,
onClickClose: PropTypes.func.isRequired,
shouldNotBlock: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired,
printRequestedItems: PropTypes.bool.isRequired,
currentLocale: PropTypes.string.isRequired,
disableSearch: PropTypes.bool,
onUpdateQuantity: PropTypes.func.isRequired,
};
NewCarton.defaultProps = {
items: [],
shipmentId: undefined,
onItemDelete: () => {},
transferRequestId: undefined,
destId: undefined,
disableSearch: false,
};
当我在输入字段中输入一次值时,父组件会重新渲染,从而无法连续输入输入字段。我想它可能需要对shouldComponentUpdate()
做些什么,但不确定究竟是什么。我有什么想法可以阻止这个吗?也许我还需要将shouldComponentUpdate
添加到子组件中?
答案 0 :(得分:0)
shouldComponentUpdate组件可能会取消更新过程。这应该用于决定是否继续。所以我不认为问题在那里,但它可能在你的onEditClicked()函数中,你执行此操作.forceUpdate()。也许设置一个受控输入,其值等于本地状态。