在React.js中执行反跳

您如何在React.js中执行反跳?

我想对handleOnChange进行反跳。

我尝试过,debounce(this.handleOnChange, 200)但没有用。

function debounce(fn, delay) {
  var timer = null;
  return function() {
    var context = this,
      args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  };
}

var SearchBox = React.createClass({
  render: function() {
    return <input type="search" name="p" onChange={this.handleOnChange} />;
  },

  handleOnChange: function(event) {
    // make ajax call
  }
});
Near小哥Green2020/03/09 23:25:07

扩展useState挂钩

import { useState } from "react";
import _ from "underscore"
export const useDebouncedState = (initialState, durationInMs = 500) => {
    const [internalState, setInternalState] = useState(initialState);
    const debouncedFunction = _.debounce(setInternalState, durationInMs);
    return [internalState, debouncedFunction];
};
export default useDebouncedState;

使用挂钩

import useDebouncedState from "../hooks/useDebouncedState"
//...
const [usernameFilter, setUsernameFilter] = useDebouncedState("")
//...
<input id="username" type="text" onChange={e => setUsernameFilter(e.target.value)}></input>

https://trippingoncode.com/react-debounce-hook/

ㄏ囧囧ㄟ2020/03/09 23:25:07

避免使用event.persist()-您想让React回收合成事件。我认为无论您使用类还是钩子,最干净的方法是将回调分为两部分:

  1. 没有反跳的回调
  2. 使用您需要的事件片段来调用去抖动的函数(以便可以回收合成事件)

班级

handleMouseOver = throttle(target => {
  console.log(target);
}, 1000);

onMouseOver = e => {
  this.handleMouseOver(e.target);
};

<div onMouseOver={this.onMouseOver} />

功能

const handleMouseOver = useRef(throttle(target => {
  console.log(target);
}, 1000));

function onMouseOver(e) {
  handleMouseOver.current(e.target);
}

<div onMouseOver={this.onMouseOver} />

请注意,如果您的handleMouseOver函数使用组件内部的状态,则应使用useMemo而不是状态useRef并将其作为依赖项传递,否则您将使用过时的数据(当然不适用于类)。

Sam老丝2020/03/09 23:25:07

Julen解决方案有点难以阅读,对于那些根据标题而不是问题的细小细节绊倒他的人,这里提供了更清晰,更准确的反应代码。

tl; dr版本:当您要更新给观察者时,请发送调用schedule方法,而该方法实际上将通知观察者(或执行ajax等)

使用示例组件jsfiddle完成jsfiddle

var InputField = React.createClass({

    getDefaultProps: function () {
        return {
            initialValue: '',
            onChange: null
        };
    },

    getInitialState: function () {
        return {
            value: this.props.initialValue
        };
    },

    render: function () {
        var state = this.state;
        return (
            <input type="text"
                   value={state.value}
                   onChange={this.onVolatileChange} />
        );
    },

    onVolatileChange: function (event) {
        this.setState({ 
            value: event.target.value 
        });

        this.scheduleChange();
    },

    scheduleChange: _.debounce(function () {
        this.onChange();
    }, 250),

    onChange: function () {
        var props = this.props;
        if (props.onChange != null) {
            props.onChange.call(this, this.state.value)
        }
    },

});
猴子宝儿神奇2020/03/09 23:25:07

您还可以使用自写的mixin,如下所示:

var DebounceMixin = {
  debounce: function(func, time, immediate) {
    var timeout = this.debouncedTimeout;
    if (!timeout) {
      if (immediate) func();
      this.debouncedTimeout = setTimeout(function() {
        if (!immediate) func();
        this.debouncedTimeout = void 0;
      }.bind(this), time);
    }
  }
};

然后像这样在组件中使用它:

var MyComponent = React.createClass({
  mixins: [DebounceMixin],
  handleClick: function(e) {
    this.debounce(function() {
      this.setState({
        buttonClicked: true
      });
    }.bind(this), 500, true);
  },
  render: function() {
    return (
      <button onClick={this.handleClick}></button>
    );
  }
});
泡芙Stafan2020/03/09 23:25:07

这是一个适用于使用TS并希望对async功能进行反跳操作的TypeScript示例

function debounce<T extends (...args: any[]) => any>(time: number, func: T): (...funcArgs: Parameters<T>) => Promise<ReturnType<T>> {
     let timeout: Timeout;

     return (...args: Parameters<T>): Promise<ReturnType<T>> => new Promise((resolve) => {
         clearTimeout(timeout);
         timeout = setTimeout(() => {
             resolve(func(...args));
         }, time)
     });
 }
猿伽罗2020/03/09 23:25:07

这里有点晚,但这应该有所帮助。创建此类(以 TypeScript编写,但易于将其转换为javascript)

export class debouncedMethod<T>{
  constructor(method:T, debounceTime:number){
    this._method = method;
    this._debounceTime = debounceTime;
  }
  private _method:T;
  private _timeout:number;
  private _debounceTime:number;
  public invoke:T = ((...args:any[])=>{
    this._timeout && window.clearTimeout(this._timeout);
    this._timeout = window.setTimeout(()=>{
      (this._method as any)(...args);
    },this._debounceTime);
  }) as any;
}

并使用

var foo = new debouncedMethod((name,age)=>{
 console.log(name,age);
},500);
foo.invoke("john",31);
逆天西门2020/03/09 23:25:07

用于throttledebounce最好的方法是创建一个函数的创造者,因此您可以任意使用它在哪里,例如:

  updateUserProfileField(fieldName) {
    const handler = throttle(value => {
      console.log(fieldName, value);
    }, 400);
    return evt => handler(evt.target.value.trim());
  }

在您的render方法中,您可以执行以下操作:

<input onChange={this.updateUserProfileField("givenName").bind(this)}/>

updateUserProfileField每次调用方法时,都会创建一个单独的函数。

请注意不要尝试直接返回处理程序,例如,这将不起作用:

 updateUserProfileField(fieldName) {
    return evt => throttle(value => {
      console.log(fieldName, value);
    }, 400)(evt.target.value.trim());
  }

之所以不起作用的原因是因为每次调用该事件都会生成一个新的节气门功能,而不是使用相同的节气门功能,所以基本上节气门将是无用的;)

另外,如果您使用debouncethrottle不需要setTimeoutclearTimeout,这实际上就是我们使用它们的原因:P

Tony阿飞2020/03/09 23:25:07

一个不错且干净的解决方案,不需要任何外部依赖项:

用React Hooks弹跳

它使用一个自定义加上React的useEffect钩子和setTimeout/ clearTimeout方法。

梅乐2020/03/09 23:25:07

与其将handleOnChange包装在debounce()中,不如将ajax调用包装在debounce中的回调函数中,从而不破坏事件对象。所以像这样:

handleOnChange: function (event) {
   debounce(
     $.ajax({})
  , 250);
}
LEY乐2020/03/09 23:25:07

只是最近反应和lodash的另一个变体。

class Filter extends Component {
  static propTypes = {
    text: PropTypes.string.isRequired,
    onChange: PropTypes.func.isRequired
  }

  state = {
    initialText: '',
    text: ''
  }

  constructor (props) {
    super(props)

    this.setText = this.setText.bind(this)
    this.onChange = _.fp.debounce(500)(this.onChange.bind(this))
  }

  static getDerivedStateFromProps (nextProps, prevState) {
    const { text } = nextProps

    if (text !== prevState.initialText) {
      return { initialText: text, text }
    }

    return null
  }

  setText (text) {
    this.setState({ text })
    this.onChange(text)
  }

  onChange (text) {
    this.props.onChange(text)
  }

  render () {
    return (<input value={this.state.text} onChange={(event) => this.setText(event.target.value)} />)
  }
}
Tony伽罗Sam2020/03/09 23:25:07

你试过了吗?

function debounce(fn, delay) {
  var timer = null;
  return function() {
    var context = this,
      args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  };
}

var SearchBox = React.createClass({
  render: function() {
    return <input type="search" name="p" onChange={this.handleOnChange} />;
  },

  handleOnChange: function(event) {
    debounce(\\ Your handleChange code , 200);
  }
});
理查德Tony2020/03/09 23:25:07

您可以使用Lodash防反跳https://lodash.com/docs/4.17.5#debounce方法。它简单有效。

import * as lodash from lodash;

const update = (input) => {
    // Update the input here.
    console.log(`Input ${input}`);     
}

const debounceHandleUpdate = lodash.debounce((input) => update(input), 200, {maxWait: 200});

doHandleChange() {
   debounceHandleUpdate(input);
}

您也可以使用以下方法取消反跳方法。

this.debounceHandleUpdate.cancel();

希望对您有帮助。干杯!!

达蒙达蒙2020/03/09 23:25:07

不受控制的组件

您可以使用event.persist()方法

下面是使用下划线的示例_.debounce()

var SearchBox = React.createClass({

  componentWillMount: function () {
     this.delayedCallback = _.debounce(function (event) {
       // `event.target` is accessible now
     }, 1000);
  },

  onChange: function (event) {
    event.persist();
    this.delayedCallback(event);
  },

  render: function () {
    return (
      <input type="search" onChange={this.onChange} />
    );
  }

});

编辑:看到此JSFiddle


受控组件

更新:上面的示例显示了不受控制的组件我一直都在使用受控元素,因此这是上面的另一个示例,但是没有使用event.persist()“特技”。

也可以使用JSFiddle没有下划线的示例

var SearchBox = React.createClass({
    getInitialState: function () {
        return {
            query: this.props.query
        };
    },

    componentWillMount: function () {
       this.handleSearchDebounced = _.debounce(function () {
           this.props.handleSearch.apply(this, [this.state.query]);
       }, 500);
    },

    onChange: function (event) {
      this.setState({query: event.target.value});
      this.handleSearchDebounced();
    },

    render: function () {
      return (
        <input type="search"
               value={this.state.query}
               onChange={this.onChange} />
      );
    }
});


var Search = React.createClass({
    getInitialState: function () {
        return {
            result: this.props.query
        };
    },

    handleSearch: function (query) {
        this.setState({result: query});
    },

    render: function () {
      return (
        <div id="search">
          <SearchBox query={this.state.result}
                     handleSearch={this.handleSearch} />
          <p>You searched for: <strong>{this.state.result}</strong></p>
        </div>
      );
    }
});

React.render(<Search query="Initial query" />, document.body);

编辑:将示例和JSFiddles更新为React 0.12

编辑:更新了示例以解决Sebastien Lorber提出的问题

编辑:使用不使用下划线且使用普通JavaScript反跳的jsfiddle更新。

飞云2020/03/09 23:25:07

如果事件对象所需的只是获取DOM输入元素,则解决方案要简单得多-只需使用即可ref请注意,这需要Underscore

class Item extends React.Component {
    constructor(props) {
        super(props);
        this.saveTitle = _.throttle(this.saveTitle.bind(this), 1000);
    }
    saveTitle(){
        let val = this.inputTitle.value;
        // make the ajax call
    }
    render() {
        return <input 
                    ref={ el => this.inputTitle = el } 
                    type="text" 
                    defaultValue={this.props.title} 
                    onChange={this.saveTitle} />
    }
}