React无状态组件clearTimeout似乎不起作用

时间:2017-06-05 16:46:55

标签: javascript reactjs redux cleartimeout

我遇到了我构建的组件的问题。如果输入两个值,则一个值(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块中调用它。这很好用。谢谢你的帮助!

1 个答案:

答案 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);
}