Javascript 在React.js中执行去盎司

Javascript 在React.js中执行去盎司,javascript,reactjs,Javascript,Reactjs,如何在React.js中执行去盎司 我想把零钱去掉 我试过使用debounce(this.handleOnChange,200),但不起作用 function debounce(fn, delay) { var timer = null; return function() { var context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function()

如何在React.js中执行去盎司

我想把零钱去掉

我试过使用
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
  }
});
功能去抖动(fn,延迟){
var定时器=null;
返回函数(){
var context=this,
args=参数;
清除超时(计时器);
计时器=设置超时(函数(){
fn.应用(上下文,args);
},延误);
};
}
var SearchBox=React.createClass({
render:function(){
返回;
},
更改:功能(事件){
//打ajax电话
}
});

与其将handleOnChange包装在debounce()中,为什么不将ajax调用包装在debounce中的回调函数中,从而不破坏事件对象呢。比如说:

handleOnChange: function (event) {
   debounce(
     $.ajax({})
  , 250);
}
非受控部件 您可以使用

下面是一个使用下划线的
..debounce()
的示例:

var SearchBox=React.createClass({
componentWillMount:函数(){
this.delayedCallback=\ u0.debounce(函数(事件){
//现在可以访问'event.target'
}, 1000);
},
onChange:函数(事件){
event.persist();
此.delayedCallback(事件);
},
渲染:函数(){
返回(
);
}
});
编辑:参见


受控元件 更新:上面的示例显示了一个。我一直在使用受控元素,所以这里是上面的另一个示例,但没有使用
event.persist()
“trickery”

A也是

var SearchBox=React.createClass({
getInitialState:函数(){
返回{
查询:this.props.query
};
},
componentWillMount:函数(){
this.handlesearchdebound=\ u0.debounce(函数(){
this.props.handleSearch.apply(this[this.state.query]);
}, 500);
},
onChange:函数(事件){
this.setState({query:event.target.value});
这个.handlesearchdebound();
},
渲染:函数(){
返回(
);
}
});
var Search=React.createClass({
getInitialState:函数(){
返回{
结果:this.props.query
};
},
handleSearch:函数(查询){
this.setState({result:query});
},
渲染:函数(){
返回(
您搜索了:{this.state.result}

); } }); React.render(,document.body);

编辑:更新了示例和JSFiddles以响应0.12

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

编辑:使用不使用下划线且使用纯javascript去Bouncing的JSFIDLE进行更新。

2019:尝试挂钩+承诺去Bouncing 这是我解决这个问题的最新版本。我将使用:

  • 要解除异步函数的隔振
  • 将去抖动的函数存储到组件中
  • 将结果输入到我的组件中
这是一些初始连接,但您正在自己编写基本块,并且您可以创建自己的自定义挂钩,这样您只需要执行一次

// Generic reusable hook
const useDebouncedSearch = (searchFunction) => {

  // Handle the input text state
  const [inputText, setInputText] = useState('');

  // Debounce the original search async function
  const debouncedSearchFunction = useConstant(() =>
    AwesomeDebouncePromise(searchFunction, 300)
  );

  // The async callback is run each time the text changes,
  // but as the search function is debounced, it does not
  // fire a new request on each keystroke
  const searchResults = useAsync(
    async () => {
      if (inputText.length === 0) {
        return [];
      } else {
        return debouncedSearchFunction(inputText);
      }
    },
    [debouncedSearchFunction, inputText]
  );

  // Return everything needed for the hook consumer
  return {
    inputText,
    setInputText,
    searchResults,
  };
};
然后你可以用你的钩子:

const useSearchStarwarsHero = () => useDebouncedSearch(text => searchStarwarsHeroAsync(text))

const SearchStarwarsHeroExample = () => {
  const { inputText, setInputText, searchResults } = useSearchStarwarsHero();
  return (
    <div>
      <input value={inputText} onChange={e => setInputText(e.target.value)} />
      <div>
        {searchResults.loading && <div>...</div>}
        {searchResults.error && <div>Error: {search.error.message}</div>}
        {searchResults.result && (
          <div>
            <div>Results: {search.result.length}</div>
            <ul>
              {searchResults.result.map(hero => (
                <li key={hero.name}>{hero.name}</li>
              ))}
            </ul>
          </div>
        )}
      </div>
    </div>
  );
};
去抖动功能可确保:

  • API调用将被取消公告
  • 取消公告函数总是返回一个承诺
  • 只有最后一个电话的回复承诺才能解决问题
  • 单个
    this.setState({result})将在每个API调用中发生
最后,如果您的组件卸载,您可能会添加另一个技巧:

componentWillUnmount() {
  this.setState = () => {};
}
请注意,可观测值(RxJS)也非常适合于去抖动输入,但它是一种更强大的抽象,可能更难正确学习/使用


<2017:仍要使用回调去抖动? 这里的重要部分是为每个组件实例创建一个单独的去抖动(或节流)函数。您不希望每次都重新创建去抖动(或节流)函数,也不希望多个实例共享同一个去抖动函数

我并没有在这个答案中定义去抖动功能,因为它实际上并不相关,但这个答案可以很好地使用下划线或lodash的
.debounce
,以及任何用户提供的去抖动功能


好主意: 因为取消公告的函数是有状态的,所以我们必须为每个组件实例创建一个取消公告的函数

ES6(类别属性):推荐

class SearchBox extends React.Component {
    method = debounce(() => { 
      ...
    });
}
ES6(类构造函数)

ES5

var SearchBox = React.createClass({
    method: function() {...},
    componentWillMount: function() {
       this.method = debounce(this.method.bind(this),100);
    },
});
请参阅:3个实例为每个实例生成1个日志条目(即全局生成3个)


这不是个好主意: 它不起作用,因为在类描述对象创建过程中,
这个
不是自己创建的对象
this.method
不会返回您期望的结果,因为
this
上下文不是对象本身(实际上还不存在,因为它刚刚被创建)


这不是个好主意: 这一次,您正在有效地创建一个取消公告的函数,该函数调用您的
This.method
。问题是每次调用
debouncedMethod
时都要重新创建它,因此新创建的debounce函数对以前的调用一无所知!随着时间的推移,必须重复使用相同的去抖动功能,否则将不会发生去抖动


这不是个好主意: 这有点棘手

该类的所有已装入实例将共享同一个取消公告的函数,通常这不是您想要的!。请参阅:3个实例全局只生成1个日志条目

您必须为每个组件实例创建一个取消公告的函数,而不是在类级别创建一个由每个组件实例共享的取消公告的函数


注意React的事件池 这是相关的,因为我们经常希望消除或限制DOM事件

class SearchBox extends React.Component {
    constructor(props) {
        super(props);
        this.method = debounce(this.method.bind(this),1000);
    }
    method() { ... }
}
var SearchBox = React.createClass({
    method: function() {...},
    componentWillMount: function() {
       this.method = debounce(this.method.bind(this),100);
    },
});
var SearchBox = React.createClass({
  method: function() {...},
  debouncedMethod: debounce(this.method, 100);
});
var SearchBox = React.createClass({
  method: function() {...},
  debouncedMethod: function() {
      var debounced = debounce(this.method,100);
      debounced();
  },
});
var SearchBox = React.createClass({
  debouncedMethod: debounce(function () {...},100),
});
onClick = e => {
  alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
  setTimeout(() => {
    alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
  }, 0);
};
onClick = e => {
  e.persist();
  alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
  setTimeout(() => {
    alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
  }, 0);
};
export class DebouncedThingy extends React.Component {
    static ToDebounce = ['someProp', 'someProp2'];
    constructor(props) {
        super(props);
        this.state = {};
    }
    // On prop maybe changed
    componentWillReceiveProps = (nextProps) => {
        this.debouncedSetState();
    };
    // Before initial render
    componentWillMount = () => {
        // Set state then debounce it from here on out (consider using _.throttle)
        this.debouncedSetState();
        this.debouncedSetState = _.debounce(this.debouncedSetState, 300);
    };
    debouncedSetState = () => {
        this.setState(_.pick(this.props, DebouncedThingy.ToDebounce));
    };
    render() {
        const restOfProps = _.omit(this.props, DebouncedThingy.ToDebounce);
        return <Thingy {...restOfProps} {...this.state} />
    }
}
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} />
    }
}
var timeout;
export default store => next => action => {
  const { meta = {} } = action;
  if(meta.debounce){
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      next(action)
    }, meta.debounce)
  }else{
    next(action)
  }
}
export default debouncedAction = (payload) => ({
  type : 'DEBOUNCED_ACTION',
  payload : payload,
  meta : {debounce : 300}
}
  updateUserProfileField(fieldName) {
    const handler = throttle(value => {
      console.log(fieldName, value);
    }, 400);
    return evt => handler(evt.target.value.trim());
  }
<input onChange={this.updateUserProfileField("givenName").bind(this)}/>
 updateUserProfileField(fieldName) {
    return evt => throttle(value => {
      console.log(fieldName, value);
    }, 400)(evt.target.value.trim());
  }
import React, {Component} from 'react'
import TextField from 'material-ui/TextField'
import { debounce } from 'lodash'

class TableSearch extends Component {

  constructor(props){
    super(props)

    this.state = {
        value: props.value
    }

    this.changeSearch = debounce(this.props.changeSearch, 250)
  }

  handleChange = (e) => {
    const val = e.target.value

    this.setState({ value: val }, () => {
      this.changeSearch(val)
    })
  }

  render() {

    return (
        <TextField
            className = {styles.field}
            onChange = {this.handleChange}
            value = {this.props.value}
        />
    )
  }
}
import React from 'react';
import ReactDOM from 'react-dom';
import {DebounceInput} from 'react-debounce-input';

class App extends React.Component {
state = {
    value: ''
};

render() {
    return (
    <div>
        <DebounceInput
        minLength={2}
        debounceTimeout={300}
        onChange={event => this.setState({value: event.target.value})} />

        <p>Value: {this.state.value}</p>
    </div>
    );
}
}

const appRoot = document.createElement('div');
document.body.appendChild(appRoot);
ReactDOM.render(<App />, appRoot);
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();
import React, {Component} from 'react';
import _ from 'lodash';

class MyComponent extends Component{
      constructor(props){
        super(props);
        this.handleChange = _.debounce(this.handleChange.bind(this),700);
      }; 
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)} />)
  }
}
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)
     });
 }
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);
import { useDebounce } from 'use-debounce';

export default function Input() {
  const [text, setText] = useState('Hello');
  const [value] = useDebounce(text, 1000);

  return (
    <div>
      <input
        defaultValue={'Hello'}
        onChange={(e) => {
          setText(e.target.value);
        }}
      />
      <p>Actual value: {text}</p>
      <p>Debounce value: {value}</p>
    </div>
  );
}
import React, { useCallback } from "react";
import { debounce } from "lodash";

const handler = useCallback(debounce(someFunction, 2000), []);

const onChange = (event) => {
    // perform any event related action here

    handler();
 };
<input>
<Debounce ms={500}>
  <List/>
</Debounce>
import React from 'react';
import Debounce from 'react-debounce-component';

class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {value: 'Hello'}
  }
  render () {
    return (
      <div>
        <input value={this.state.value} onChange={(event) => {this.setState({value: event.target.value})}}/>
        <Debounce ms={1000}>
          <div>{this.state.value}</div>
        </Debounce>
      </div>
    );
  }
}

export default App;
import React, { useCallback } from "react";
import { debounce } from "lodash";

import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';

const debounceTimeInMS = 2000;

export const PrimaryButtonDebounced = (props) => {

    const debouncedOnClick = debounce(props.onClick, debounceTimeInMS, { leading: true });

    const clickHandlerDebounced = useCallback((e, value) => {

        debouncedOnClick(e, value);

    },[]);

    const onClick = (e, value) => {

        clickHandlerDebounced(e, value);
    };

    return (
        <PrimaryButton {...props}
            onClick={onClick}
        />
    );
}
import React, { Component } from 'react'

const DEBOUNCE_TIME = 500

class PlacesAutocomplete extends Component {
  debounceTimer = null;

  onChangeHandler = (event) => {
    // Clear the last registered timer for the function
    clearTimeout(this.debounceTimer);

    // Set a new timer
    this.debounceTimer = setTimeout(
      // Bind the callback function to pass the current input value as arg
      this.getSuggestions.bind(null, event.target.value), 
      DEBOUNCE_TIME
    )
  }

  // The function that is being debounced
  getSuggestions = (searchTerm) => {
    console.log(searchTerm)
  }

  render() {
    return (
      <input type="text" onChange={this.onChangeHandler} />
    )
  }
}

export default PlacesAutocomplete
import React, { Component } from 'react'
import DebouncedInput from '../DebouncedInput';

class PlacesAutocomplete extends Component {
  debounceTimer = null;

  getSuggestions = (searchTerm) => {
    console.log(searchTerm)
  }

  render() {
    return (
      <DebouncedInput debounceTime={500} callback={this.getSuggestions} />
    )
  }
}

export default PlacesAutocomplete
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);
  }
});
const debounce = (fn, delay) => {
    let timer = null;
    return function() {
        const context = this,
        args = arguments;
        clearTimeout(timer);
        timer = setTimeout(() => {
            fn.apply(context, args);
        }, delay);
    };  
}

const [search, setSearch] = useState('');
const [searchFor, setSearchFor] = useState(search);

useEffect(() => {
    console.log("Search:", searchFor);
}, [searchFor]);

const fireChange = event => {
    const { keyCode } = event;
    if (keyCode === 13) {
        event.preventDefault();
        setSearchFor(search);
    }
}

const changeSearch = event => {
    const { value } = event.target;
    setSearch(value);
    debounceSetSearchFor(value);
};

const debounceSetSearchFor = useCallback(debounce(function(value) {
    setSearchFor(value);
}, 250), []);
<input value={search} onKeyDown={fireChange} onChange={changeSearch}  />
function SearchInput() {
  const [realTimeValue, setRealTimeValue] = useState('');

  const debouncedValue = useDebouncedValue(realTimeValue, 500); // this value will pick real time value, but will change it's result only when it's seattled for 500ms

  useEffect(() => {
    // this effect will be called on seattled values
    api.fetchSearchResults(debouncedValue);
  }, [debouncedValue])

  return <input onChange={event => setRealTimeValue(event.target.value)} />
}
import { useState, useEffect } from "react";

export function useDebouncedValue<T>(input: T, time = 500) {
  const [debouncedValue, setDebouncedValue] = useState(input);

  // every time input value has changed - set interval before it's actually commited
  useEffect(() => {
    const timeout = setTimeout(() => {
      setDebouncedValue(input);
    }, time);

    return () => {
      clearTimeout(timeout);
    };
  }, [input, time]);

  return debouncedValue;
}
function AlertButton() {
  function showAlert() {
    alert('Clicking has seattled');
  }

  const debouncedShowAlert = useDebouncedCallback(showAlert, 500);

  return <button onClick={debouncedShowAlert}>Click</button>
}
import debounce from 'lodash/debounce';
import { useMemo } from 'react';

export function useDebouncedCallback<T extends (...args: any) => any>(callback: T, wait?: number) {
  const debouncedCallback = useMemo(() => debounce(callback, wait), [callback, wait]);

  return debouncedCallback;
}
const PageOne = () => {
 const [requesting, setRequesting] = useState(false);

  useEffect(() => {
    return () => {
      setRequesting(false);
    };
  }, [requesting]);

  const onDebounce = (e) => {
    if (requesting === true) {
      e.preventDefault();
    }
    // ACTIONS
    setLoading(true);
  };

 return (
  <div>
    
    <button onClick={onDebounce}>Requesting data</button>
  </div>
 )
}

import {useState} from "react";

const useDebounce = ({defaultTimeout = 250, defaultIdentifier = 'default'} = {}) => {

    const [identifiers, setIdentifiers] = useState({[defaultIdentifier]: null});

    return ({fn = null, identifier = defaultIdentifier, timeout = defaultTimeout} = {}) => {
        if (identifiers.hasOwnProperty(identifier)) clearTimeout(identifiers[identifier]);
        setIdentifiers({...identifiers, [identifier]: setTimeout(fn, timeout)});
    };
};

export default useDebounce;
const debounce = useDebounce();

const handlerA = () => {
    debounce({fn: () => console.log('after 2000ms of last call with identifier A'), identifier: 'A', timeout: 2000});
};

const handlerB = () => {
    debounce({fn: () => console.log('after 1500ms of last call with identifier B'), identifier: 'B', timeout: 1500});
};
const [query, setQuery] = useState('');

useDebounce(
  () => {
    emitYourOnDebouncedSearchEvent(query);
  },
  2000,
  [query]
);

return <input onChange={({ currentTarget }) => setQuery(currentTarget.value)} />