Perform debounce in React.js

前端 未结 30 1590
礼貌的吻别
礼貌的吻别 2020-11-22 04:11

How do you perform debounce in React.js?

I want to debounce the handleOnChange.

I tried with debounce(this.handleOnChange, 200) but it doesn\'t

相关标签:
30条回答
  • 2020-11-22 04:34

    This solution does not need any extra lib and it also fires things up when the user presses enter:

    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), []);
    

    and the input could be like:

    <input value={search} onKeyDown={fireChange} onChange={changeSearch}  />
    
    0 讨论(0)
  • 2020-11-22 04:34

    Here's a working TypeScript example for those who use TS and want to debounce async functions.

    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)
         });
     }
    
    0 讨论(0)
  • 2020-11-22 04:34

    Avoid using event.persist() - you want to let React recycle the synthetic event. I think the cleanest way whether you use classes or hooks is to split the callback into two pieces:

    1. The callback with no debouncing
    2. Calls a debounced function with only the pieces of the event you need (so the synthetic event can be recycled)

    Classes

    handleMouseOver = throttle(target => {
      console.log(target);
    }, 1000);
    
    onMouseOver = e => {
      this.handleMouseOver(e.target);
    };
    
    <div onMouseOver={this.onMouseOver} />
    

    Functions

    const handleMouseOver = useRef(throttle(target => {
      console.log(target);
    }, 1000));
    
    function onMouseOver(e) {
      handleMouseOver.current(e.target);
    }
    
    <div onMouseOver={this.onMouseOver} />
    

    Note that if your handleMouseOver function uses state from within the component, you should use useMemo instead of useRef and pass those as dependencies otherwise you will be working with stale data (does not apply to classes of course).

    0 讨论(0)
  • 2020-11-22 04:35

    Here is an example I came up with that wraps another class with a debouncer. This lends itself nicely to being made into a decorator/higher order function:

    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} />
        }
    }
    
    0 讨论(0)
  • 2020-11-22 04:37

    2019: Use the 'useCallback' react hook

    After trying many different approaches, I found using useCallback to be the simplest and most efficient at solving the multiple calls problem of using debounce within an onChange event.

    As per the Hooks API documentation,

    useCallback returns a memorized version of the callback that only changes if one of the dependencies has changed.

    Passing an empty array as a dependency makes sure the callback is called only once. Here's a simple implementation :

    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();
     };
    

    Hope this helps!

    0 讨论(0)
  • 2020-11-22 04:37

    There is now another solution for React and React Native in late/2019:

    react-debounce-component

    <input>
    <Debounce ms={500}>
      <List/>
    </Debounce>
    

    It's a component, easy to use, tiny and widley supported

    Example:

    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;
    

    *I'm the creator of this component

    0 讨论(0)
提交回复
热议问题