Perform debounce in React.js

前端 未结 30 1541
礼貌的吻别
礼貌的吻别 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:26

    Just another variant with recent react and 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)} />)
      }
    }
    
    0 讨论(0)
  • 2020-11-22 04:26

    I was searching for a solution to the same problem and came across this thread as well as some others but they had the same problem: if you are trying to do a handleOnChange function and you need the value from an event target, you will get cannot read property value of null or some such error. In my case, I also needed to preserve the context of this inside the debounced function since I'm executing a fluxible action. Here's my solution, it works well for my use case so I'm leaving it here in case anyone comes across this thread:

    // at top of file:
    var myAction = require('../actions/someAction');
    
    // inside React.createClass({...});
    
    handleOnChange: function (event) {
        var value = event.target.value;
        var doAction = _.curry(this.context.executeAction, 2);
    
        // only one parameter gets passed into the curried function,
        // so the function passed as the first parameter to _.curry()
        // will not be executed until the second parameter is passed
        // which happens in the next function that is wrapped in _.debounce()
        debouncedOnChange(doAction(myAction), value);
    },
    
    debouncedOnChange: _.debounce(function(action, value) {
        action(value);
    }, 300)
    
    0 讨论(0)
  • 2020-11-22 04:30

    for throttle or debounce the best way is to create a function creator so you can use it any where, for example:

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

    and in your render method you can do:

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

    the updateUserProfileField method will create a separated function each time you call it.

    Note don't try to return the handler directly for example this will not work:

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

    the reason why this will not work because this will generate a new throttle function each time the event called instead of using the same throttle function, so basically the throttle will be useless ;)

    Also if you use debounce or throttle you don't need setTimeout or clearTimeout, this is actually why we use them :P

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

    Here's a snippet using @Abra's approach wrapped in a function component (we use fabric for the UI, just replace it with a simple button)

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

    A nice and clean solution, that doesn't require any external dependencies:

    Debouncing with React Hooks

    It uses a custom plus the useEffect React hooks and the setTimeout / clearTimeout method.

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

    My solution is hooks based (written in Typescript).

    I've got 2 main hooks useDebouncedValue and useDebouncedCallback

    First - useDebouncedValue

    Let's say we've got a search box, but we want to ask the server for search results after the user has stopped typing for 0,5s

    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)} />
    }
    

    Implementation

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

    Second useDebouncedCallback

    It just creates a 'debounced' function in the scope of your component.

    Let's say we've got a component with a button that will show alert 500ms after you stopped clicking it.

    function AlertButton() {
      function showAlert() {
        alert('Clicking has seattled');
      }
    
      const debouncedShowAlert = useDebouncedCallback(showAlert, 500);
    
      return <button onClick={debouncedShowAlert}>Click</button>
    }
    

    Implementation (note I'm using lodash/debounce as a helper)

    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;
    }
    
    0 讨论(0)
提交回复
热议问题