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

标签 javascript reactjs redux cleartimeout

我在构建组件时遇到问题。如果输入了两个值,则一个值 (inclVal) 必须大于另一个值 (exclVal)。我想运行使用 setTimeout() 处理此问题的函数,以便它在 props 停止更改后不会更新一秒钟,以确保它不会更改用户输入的值她正在进入它。为此,我在 else block 中放入了一个 clearTimeout() 以防止函数在 props 更改时执行,从而使其变得多余。问题是 clearTimeout() 由于某种原因无法正常工作,并且只要输入 if block ,更新功能就会运行,即使 else block 正在超时间隔内进入。

该组件是一个无状态功能组件,使用 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 block 的顶部和 else block 中调用它。效果很好。感谢您的帮助!

最佳答案

实际的问题是,每次您的组件呈现时,它都会生成 inclEntryTimer实例(以及所有其他局部变量),因此无法进行后续调用清除上一次通话中开始的超时。

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

更新:

根据您的尝试,真正的问题是您没有在每次 Prop 更改时清除旧定时器。所以想一想如果 Prop 发生变化并且你启动一个计时器会发生什么, Prop 再次变化并且它仍然更高所以你启动第二个计时器并且永远不会清除第一个, Prop 再次变化并且你启动第三个计时器等等。最后, Prop 改变了,你停止了最后一个计时器。但是前 5 个计时器仍在运行。因此,每次启动新计时器时都应清除现有计时器。

但是,如果您稍稍退后一步……您不需要自己实现此模式。你正在做的是所谓的“去抖动”。所以用别人的debounce算法。

以下是使用 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);
}

关于javascript - React 无状态组件 clearTimeout 似乎不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44373723/

相关文章:

javascript - 无限移动鼠标

javascript - 如何用字符串数组(js)搜索字符串?

javascript - react .js : handleSubmit or onClick not triggered when rendered on the server side

reactjs - 在 React 测试库中测试点击事件

reactjs - 如何在redux中更新数组内的单个值

reactjs - 如何依次调度多个 Action

reactjs - 为什么初始状态为null时createSlice会报错?

javascript - 按时间间隔运行 Javascript?

javascript - 如何创建一个使用本地存储的 javascript ipad 应用程序?

javascript - 在react-router路由中使splat可选