1

我构建的组件有问题。如果两者都输入,一个值 ( inclVal) 必须大于另一个 ( )。exclVal我想用 a 运行处理此问题的函数,setTimeout()以便在道具停止更改后它不会更新一秒钟,以确保它不会更改用户在输入时输入的值。为此,我clearTimeout()在一个elseblock 中放入了 a ,以防止函数在 props 发生变化时执行,从而使其冗余。问题是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。效果很好。谢谢您的帮助!

4

1 回答 1

2

实际的问题是,每次您的组件呈现时,它都会生成一个(以及所有其他局部变量)的实例,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);
}
于 2017-06-05T16:53:35.537 回答