DatePicker 组件卡死问题总结
问题描述
在 React 项目中使用 DatePicker
组件时,遇到了页面卡死的问题。组件的主要功能是使用 Ant Design 的 RangePicker
组件来选择日期范围,并将日期格式从格里高利历转换为 Moment.js 格式。
当用户在页面上交互 DatePicker
组件时,浏览器出现卡死现象,导致用户无法正常操作页面。
原因分析
经过调查和调试,发现问题主要集中在以下几个方面:
-
不必要的重渲染:
- 组件在每次
onChange
事件触发时,会更新state
,导致整个组件重新渲染。 componentWillReceiveProps
接收到数据不同后,触发组件重新渲染,使用不当会导致组件不必要的更新和重渲染。
- 组件在每次
-
状态同步问题:
- 直接在
handleDateChange
方法中更新tempValue
,会导致状态在短时间内频繁变化,增加了浏览器的负担。 - 导致状态更新的冲突和性能问题。
- 直接在
解决方案
为了解决以上问题,我们采取了以下措施:
-
componentWillReceiveProps
:
componentWillReceiveProps
添加条件判断,防止无限循环。 -
引入
tempSelectedValue
作为中间状态:- 在
handleDateChange
方法中使用tempSelectedValue
来暂存用户的输入,减少直接更新tempValue
带来的频繁重渲染。 - 只有在必要时才更新
tempValue
,确保状态变化的稳定性和一致性。
- 在
改进后的代码
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { DatePicker } from 'antd';
import moment from 'moment';
import _ from 'lodash';
import './style.less';
const { RangePicker } = DatePicker;
function transformGregorianToMoment(format) {
return _.chain(format).replace('yyyy', 'YYYY').replace('dd', 'DD').value();
}
export default class DateInput extends Component {
static propTypes = {
size: PropTypes.string, // antd Input size
format: PropTypes.string.isRequired, // gregorian format
locale: PropTypes.object,
style: PropTypes.object,
value: PropTypes.array,
onChange: PropTypes.func,
disabledDate: PropTypes.func,
};
static defaultProps = {
size: 'default',
value: undefined,
locale: {},
style: {},
onChange: _.noop,
};
constructor(props) {
super(props);
this.state = {
tempValue: props.value,
tempSelectedValue: props.value,
};
}
componentWillReceiveProps(nextProps, prevState) {
if (!_.isEqual(nextProps.value, prevState.tempValue)) {
return {
tempValue: nextProps.value,
tempSelectedValue: nextProps.value,
};
}
return null;
}
handleDateChange = (mDate) => {
const { onChange } = this.props;
this.setState({ tempValue: mDate });
onChange(mDate);
};
render() {
const {
size, style, format, locale, disabledDate,
} = this.props;
const { tempValue, tempSelectedValue } = this.state;
const momentFormat = transformGregorianToMoment(format);
const [start = new Date(), end = new Date()] = tempSelectedValue || []
return (
<span
className="dateInput"
style={{
...style,
minWidth: 208,
display: 'inline-block',
verticalAlign: 'top',
}}
>
<RangePicker
showTime
size={size}
value={[moment(start), moment(end)]}
format={momentFormat}
disabledDate={disabledDate}
onChange={this.handleDateChange}
/>
</span>
);
}
}
总结
通过引入 tempSelectedValue
作为中间状态,使用 getDerivedStateFromProps
方法代替过时的生命周期方法,有效地解决了组件重渲染和状态同步问题,成功避免了浏览器的卡死现象。这个方法不仅提高了组件的性能,还增强了代码的可维护性和可读性。