我遇到了我构建的组件的问题。如果输入两个值,则一个值(inclVal
)必须大于另一个值exclVal
)。我想运行一个用setTimeout()
处理此功能的函数,这样在道具停止更改后它不会更新一秒,以确保它不会改变用户输入的值。进入它。为此,我在clearTimeout()
块中放入了一个else
,以防止在道具发生变化时执行该函数以使其冗余。问题是clearTimeout()
由于某种原因而无法正常工作,只要输入if
块,即使在超时内输入else
块,更新功能也会运行间隔。
该组件是无状态功能组件,并使用redux进行状态管理。我已经读了很多关于如何使这些东西发挥作用,但似乎没有任何帮助。任何帮助表示赞赏!
以下是代码:
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import SelectPickerPulldown from '../../components/SelectPickerPulldown'
import TextInput from '../../components/TextInput'
import { odOptions } from '../../config/'
import { setODProperty } from '../../actions/odAnalyzerActions'
import { getConversion, getGreaterVal } from '../../utils/'
const InclusionExclusionOptions = ({ name,
analysisPoint,
paths,
exclVal,
exclUnit,
inclVal,
inclUnit,
getVal,
getUnit,
setSafeInclVal,
}) => {
const disabled = analysisPoint !== null || paths ? false : true
let inclEntryTimer = null
const exclCompare = getConversion(exclUnit)(exclVal)
const inclCompare = getConversion(inclUnit)(inclVal)
if (exclVal > 0 && inclVal > 0 && exclCompare > inclCompare) {
const safeInclVal = getGreaterVal(exclVal, exclUnit, inclUnit)
console.log('entering timeout');
inclEntryTimer = setTimeout( () => {
console.log('dispatching timeout action');
setSafeInclVal(safeInclVal)
}, 1000)
}
else {
console.log('clearing timeout');
clearTimeout(inclEntryTimer)
inclEntryTimer = null
}
return (
<div className="form-group" >
<h4>Inclusion/Exclusion Options</h4>
<ul className={name}>
<li className={`text-select-group ${disabled ? name + ' disabled' : name}`}>
<p>Exclusion Radius</p>
<div className="radius-setting">
<TextInput
type="number"
name="exclVal"
value={exclVal}
onChange={getVal}
/>
<SelectPickerPulldown
value={'exclUnit'}
options={odOptions.units}
selected={exclUnit}
getSelected={getUnit}
/>
</div>
</li>
<li className={`text-select-group ${disabled ? name + ' disabled' : name}`}>
<p>Inclusion Radius</p>
<div className="radius-setting">
<TextInput
type="number"
name="inclVal"
value={inclVal}
onChange={getVal}
/>
<SelectPickerPulldown
value={'inclUnit'}
options={odOptions.units}
selected={inclUnit}
getSelected={getUnit}
/>
</div>
</li>
</ul>
</div>
)
}
InclusionExclusionOptions.propTypes = {
name: PropTypes.string,
exclVal: PropTypes.number,
exclUnit: PropTypes.string,
inclVal: PropTypes.number,
inclUnit: PropTypes.string,
getVal: PropTypes.func,
getUnit: PropTypes.func,
}
const mapStateToProps = (state, ownProps) => {
const name = 'inclusion-exclusion-options'
const { analysisPoint,
paths,
exclVal,
exclUnit,
inclVal,
inclUnit } = state.odAnalyzerState
return {
name,
analysisPoint,
paths,
exclVal,
exclUnit,
inclVal,
inclUnit,
}
}
const mapDispatchToProps = dispatch => {
return {
getUnit: option => dispatch(setODProperty(option)),
getVal: (e, name) => dispatch(setODProperty({[name]: parseInt(e.target.value)})),
setSafeInclVal: safeInclVal => dispatch(setODProperty({inclVal: safeInclVal}))
}
}
export default connect(
mapStateToProps,
mapDispatchToProps)(InclusionExclusionOptions)
以下是使用componentDidUpdate()
的类组件的更新代码:
class InclusionExclusionOptions extends React.Component{
constructor(props){
super(props)
this.inclEntryTimer = null
}
componentDidUpdate(props){
const { exclVal,
exclUnit,
inclVal,
inclUnit,
} = this.props
const exclCompare = getConversion(exclUnit)(exclVal)
const inclCompare = getConversion(inclUnit)(inclVal)
if (!!exclVal && !!inclVal && exclCompare > inclCompare) {
const safeInclVal = getGreaterVal(exclVal, exclUnit, inclUnit)
console.log('entering timeout')
this.inclEntryTimer = setTimeout( () => {
console.log('dispatching timeout action');
this.props.setSafeInclVal(safeInclVal)
}, 3000)
}
else {
console.log('clearing timeout');
clearTimeout(this.inclEntryTimer)
this.inclEntryTimer = null
}
}
render() {
const { name,
analysisPoint,
paths,
exclVal,
exclUnit,
inclVal,
inclUnit,
getVal,
getUnit,
} = this.props
const disabled = analysisPoint !== null || paths ? false : true
return (
<div className="form-group" >
<h4>Inclusion/Exclusion Options</h4>
<ul className={name}>
<li className={`text-select-group ${disabled ? name + ' disabled' : name}`}>
<p>Exclusion Radius</p>
<div className="radius-setting">
<TextInput
type="number"
name="exclVal"
value={exclVal}
onChange={getVal}
/>
<SelectPickerPulldown
value={'exclUnit'}
options={odOptions.units}
selected={exclUnit}
getSelected={getUnit}
/>
</div>
</li>
<li className={`text-select-group ${disabled ? name + ' disabled' : name}`}>
<p>Inclusion Radius</p>
<div className="radius-setting">
<TextInput
type="number"
name="inclVal"
value={inclVal}
onChange={getVal}
/>
<SelectPickerPulldown
value={'inclUnit'}
options={odOptions.units}
selected={inclUnit}
getSelected={getUnit}
/>
</div>
</li>
</ul>
</div>
)
}
}
根据Brandon的建议,我可以通过在重新声明之前清除超时来清除超时。我打破了一个明确的超时功能
clearInclEntryTimer(){
clearTimeout(this.inclEntryTimer)
this.inclEntryTimer = null
}
然后在if
块的顶部和else
块中调用它。这很好用。谢谢你的帮助!
答案 0 :(得分:2)
实际问题是,每次组件呈现时,它都会生成{em>新 inclEntryTimer
实例(以及所有其他局部变量),因此后续调用无法清除超时在之前的通话中开始。
概念性问题是您的组件是有状态,而不是无状态。您的要求是组件需要跟踪时间状态(特别是计时器)。将无状态组件更改为传统的有状态组件,您将能够将计时器ID存储为类实例的属性。然后,如果满足条件,您可以使用componentDidUpdate(prevProps)
生命周期事件来清除超时。
更新
根据您的尝试,真正的问题是您不会在每次改变道具时清除旧的计时器。所以想想如果道具发生变化并且你开始计时器会发生什么,道具会再次变化并且它仍然更高,所以你开始第二个计时器并且从不清除第一个,道具再次改变,你开始第三个计时器,依此类推。最后道具改变,你停止最后一个计时器。但前5个计时器仍在运行。因此,每次开始新的计时器时都应该清除现有的计时器。
但如果你稍微退一步问题......你不需要自己实现这个模式。你正在做的是被称为“去抖动”的东西。所以使用别人的去抖算法。
以下是如何使用lodash:
import debounce from 'lodash/debounce';
export default class Component from React.Component {
componentDidMount() {
this.correctValues(this.props);
}
componentDidUpdate() {
this.correctValues(this.props);
}
componentWillUnmount() {
// prevent the debounced method from running after we unmount
this._unmounted = true;
}
render() {
return <div>...</div>;
}
// we use debounce to essentially only run this function 3000 ms after
// it is called. If it gets called a 2nd time, stop the first timer
// and start a new one. and so on.
correctValues = debounce(props => {
// make sure we are still mounted
if (!this._unmounted) {
// need to correct the values!
if (props.a < props.b) {
props.setCorrectValue(props.a);
}
}
}, 3000);
}