2017-06-05 328 views
1

我遇到了我构建的组件的问题。如果两者都输入,则一个值(inclVal)必须大于另一个值(exclVal)。我想运行一个处理setTimeout()的函数,这样在道具停止更改后它不会更新一秒,以确保它在用户输入时不会更改用户输入的值。为此,我在else块中放置了一个clearTimeout(),以防止在道具改变时使该功能执行以使其冗余。问题在于clearTimeout()由于某些原因不起作用,只要输入if块,即使在超时间隔内输入了else块,更新功能仍在运行。React无状态组件clearTimeout似乎不起作用

该组件是一个无状态的功能组件,并且正在使用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> 
    ) 
    } 
} 

每布兰登的建议下,我能得到超时只需清除清除它在宣布它之前。我打破了一个明确的超时功能

clearInclEntryTimer(){ 
    clearTimeout(this.inclEntryTimer) 
    this.inclEntryTimer = null 
    } 

然后把它称为在if块的顶部和else块。这很好。谢谢您的帮助!

+2

你不应该像'setTimeout'表演渲染方法的内部副作用(和功能部件是有效_just_渲染方法)。如果你想做这样的事情,这应该是一个真正的类组件,而行为应该放在组件生命周期方法中。 – markerikson

回答

2

实际的问题是,每次你的组件呈现,它产生的inclEntryTimer一个实例(和所有其他的局部变量)等有没有办法让后续调用永远清除超时以前调用开始。

概念性的问题是,你的组件是状态,不无国籍。您的要求是组件需要将时间作为状态(特别是计时器)进行跟踪。将无状态组件更改为传统的有状态组件,并且可以将该定时器ID存储为类实例的属性。如果满足条件,则可以使用componentDidUpdate(prevProps)生命周期事件清除超时。

更新:根据您试了一下

,真正的问题是,你不清除每个道具变化的老前辈。因此,想想如果道具改变并且你启动了一个计时器,道具会再次改变并且它仍然更高,所以你开始第二个计时器并且永远不会清除第一个,道具再次改变并且你开始第三个计时器等等。最后道具改变,你停止最后一个计时器。但前5名计时员仍在跑步。所以你应该每次开始一个新的计时器时清除现有的计时器。

但是,如果你稍微退一步......你不需要自己实现这个模式。你正在做的是被称为“debouncing”的东西。所以使用别人的去抖动算法。

以下是如何与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); 
} 
+0

好的,那就是我担心的事情,尽管即使是有状态的类组件,我也使用'componentWillReceiveProps()'和'setState()'来存储定时器,然后(理论上)取消它。我似乎无法取消它,以回应道具改变。 – matsad

+0

在componentDidMount和componentDidUpdate中执行超时逻辑。不要将定时器ID存储在setState中。只需将它作为课堂中的一个字段存储即可。除此之外,显示您的更新代码。 – Brandon

+0

谢谢,刚开始尝试它,超时仍未清除。我不明白使用'componentDidMount()'作为超时函数来运行或在道具更改时被清除。下面是代码: – matsad