阅读量:0
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
方法代替过时的生命周期方法,有效地解决了组件重渲染和状态同步问题,成功避免了浏览器的卡死现象。这个方法不仅提高了组件的性能,还增强了代码的可维护性和可读性。