2017-06-05 18 views
1

私が作成したコンポーネントに問題があります。両方の値が入力された場合、1つの値(inclVal)は別の値(exclVal)より大きい必要があります。私はsetTimeout()でこれを処理する関数を実行して、ユーザーが入力中に入力した値を変更しないように小道具の変更をやめた後に1秒間は更新しないようにしたかったのです。この目的のために、私はelseブロック内にclearTimeout()を入れて、小道具が冗長になるように変更された場合には機能が実行されないようにします。問題は何らかの理由でclearTimeout()が動作していないことと、elseブロックがタイムアウト間隔内に入力されていても、ifブロックが入力されるたびに更新機能が実行されていることです。状態を保持しないコンポーネントが反応しない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

あなたが(と機能性成分が効果的にレンダリングする方法を_just_れる)renderメソッドの内部setTimeout' 'のような副作用を実行するべきではありません。このようなことをしたい場合は、代わりにクラスコンポーネントにする必要があり、その動作はコンポーネントのライフサイクルメソッドに入れる必要があります。 – markerikson

答えて

2

実際の問題は、それがinclEntryTimer新しいインスタンス(および他のすべてのローカル変数)を生成し、その前の呼び出しで始まった、これまでのタイムアウトをクリアする後続の呼び出しのための方法はありません、あなたのコンポーネントがレンダリングする毎回です。

概念的な問題は、あなたのコンポーネントがステートフルであることではないステートレスです。要件は、コンポーネントが状態(具体的にはタイマー)として時間を追跡する必要があるようなものです。ステートレスコンポーネントを従来のステートフルコンポーネントに変更すると、タイマーIDをクラスインスタンスのプロパティとして保存することができます。条件が満たされた場合は、componentDidUpdate(prevProps)ライフサイクルイベントを使用してタイムアウトをクリアすることができます。

更新:あなたが試したものに基づい

、本当の問題は、あなたがすべての小道具変化に古いタイマーをクリアしていないことです。それで、小道具が変わって、あなたがタイマーを始めると、小道具が再び変化し、それはまだ高いので、第2のタイマーを開始し、最初のものを決してクリアしないで、小道具が再び変化し、第3のタイマーなどを始めるので、最後に小道具が変わり、最後のタイマーを止めます。しかし、最初の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); 
} 
+0

それはステートフルなクラスコンポーネントであっても、私が思っていたことですが、 'componentWillReceiveProps()'と 'setState()'を使って同じ結果を得て、タイマーを保存してから(理論的に)キャンセルします。私は小道具の変更に応じてキャンセルすることはできないようです。 – matsad

+0

componentDidMountとcomponentDidUpdateでタイムアウトロジックを実行します。 setStateにタイマーIDを格納しないでください。それをクラスのフィールドとして保存するだけです。それ以外は、更新されたコードを表示してください。 – Brandon

+0

ありがとう、ちょうど今それを試して、タイムアウトはまだクリアされていない。私は、 'componentDidMount()'を使って、タイムアウト関数を実行したり、小道具の変更に応じてクリアすることを理解していません。ここでは、コードは次のとおり – matsad

関連する問題