ดำเนินการ debounce ใน React.js


496

คุณจะทำการ debounce ใน React.js ได้อย่างไร

ฉันต้องการ debounce 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
  }
});

ผมได้พบกับปัญหาเดียวกันกับคุณคำตอบที่ยอดเยี่ยมด้านล่าง! debounceแต่ฉันคิดว่าคุณใช้ในทางที่ผิดของ ที่นี่เมื่อใดonChange={debounce(this.handleOnChange, 200)}/>มันจะเรียกใช้debounce functionทุกครั้ง แต่ที่จริงแล้วสิ่งที่เราต้องการคือเรียกใช้ฟังก์ชันที่ฟังก์ชัน debounce ส่งคืน
pingfengafei

คำตอบ:


834

2019: ลอง hooks + contract debouncing

นี่เป็นเวอร์ชันล่าสุดของวิธีแก้ไขปัญหานี้ ฉันจะใช้:

  • awesome-debounce-contractเพื่อ debounce ฟังก์ชั่น async
  • ใช้ค่าคงที่เพื่อจัดเก็บฟังก์ชันที่ลบล้างลงในส่วนประกอบ
  • react-async-hookเพื่อให้ได้ผลลัพธ์ในองค์ประกอบของฉัน

นี่คือการเดินสายเริ่มต้นบางส่วน แต่คุณกำลังเขียนบล็อกดั้งเดิมด้วยตัวคุณเองและคุณสามารถสร้างเบ็ดแบบกำหนดเองของคุณเองเพื่อที่คุณจะต้องทำสิ่งนี้เพียงครั้งเดียว

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

คุณจะพบตัวอย่างนี้ทำงานที่นี่และคุณควรอ่านเอกสารตอบกลับแบบ async-hookสำหรับรายละเอียดเพิ่มเติม


2018: ลองสัญญาว่าจะเปิดตัว

บ่อยครั้งที่เราต้องการเรียกคืนการเรียก API เพื่อหลีกเลี่ยงแบ็กเอนด์ที่มีคำขอที่ไร้ประโยชน์

ในปีพ. ศ. 2561 การทำงานกับการโทรกลับ (Lodash / Underscore) รู้สึกแย่และมีข้อผิดพลาดเกิดขึ้นกับฉัน มันเป็นเรื่องง่ายที่จะพบปัญหาสำเร็จรูปและการทำงานพร้อมกันเนื่องจากการเรียก API การแก้ไขตามลำดับโดยพลการ

ฉันได้สร้างห้องสมุดเล็ก ๆ โดยมีปฏิกิริยาตอบสนองเพื่อแก้ไขความเจ็บปวดของคุณ: คำสัญญาที่ยอดเยี่ยม

สิ่งนี้ไม่ควรซับซ้อนกว่านั้น:

const searchAPI = text => fetch('/search?text=' + encodeURIComponent(text));

const searchAPIDebounced = AwesomeDebouncePromise(searchAPI, 500);

class SearchInputAndResults extends React.Component {
  state = {
    text: '',
    results: null,
  };

  handleTextChange = async text => {
    this.setState({ text, results: null });
    const result = await searchAPIDebounced(text);
    this.setState({ result });
  };
}

ฟังก์ชั่น debounce ทำให้มั่นใจได้ว่า:

  • การเรียก API จะถูกหักล้าง
  • ฟังก์ชั่น debounce มักจะส่งกลับสัญญา
  • เฉพาะสัญญาที่ส่งคืนล่าสุดของสายที่จะแก้ไข
  • เพียงหนึ่งเดียวthis.setState({ result });ที่จะเกิดขึ้นต่อการโทร API

ในที่สุดคุณอาจเพิ่มเคล็ดลับอื่นถ้าองค์ประกอบของคุณ unmount:

componentWillUnmount() {
  this.setState = () => {};
}

โปรดสังเกตว่าObservables (RxJS) สามารถเป็นตัวเลือกที่ยอดเยี่ยมสำหรับการเปิดใช้งานอินพุต แต่มันเป็นนามธรรมที่ทรงพลังยิ่งกว่าซึ่งอาจยากต่อการเรียนรู้ / ใช้อย่างถูกต้อง


<2017: ยังต้องการใช้การเรียกคืนการโทรกลับหรือไม่

ส่วนที่สำคัญที่นี่คือการสร้างเดียว debounced (หรือผ่อนคันเร่ง) ฟังก์ชั่นต่อเช่นองค์ประกอบ คุณไม่ต้องการสร้างฟังก์ชัน debounce (หรือ throttle) ใหม่ทุกครั้งและคุณไม่ต้องการให้หลาย ๆ อินสแตนซ์แบ่งปันฟังก์ชัน debounce เดียวกัน

ฉันไม่ได้นิยามฟังก์ชัน debouncing ในคำตอบนี้เนื่องจากมันไม่เกี่ยวข้องกันจริงๆ แต่คำตอบนี้จะทำงานได้อย่างสมบูรณ์แบบด้วย_.debounceการขีดเส้นใต้หรือ lodash รวมถึงฟังก์ชัน debouncing ที่ผู้ใช้จัดหา


ความคิดที่ดี:

เนื่องจากฟังก์ชั่นที่หักล้างนั้นเป็นของรัฐเราจึงต้องสร้างฟังก์ชั่นหักล้างหนึ่งรายการต่ออินสแตนซ์ขององค์ประกอบต่อหนึ่งเช่นองค์ประกอบ

ES6 (คุณสมบัติคลาส) : แนะนำ

class SearchBox extends React.Component {
    method = debounce(() => { 
      ...
    });
}

ES6 (ตัวสร้างคลาส)

class SearchBox extends React.Component {
    constructor(props) {
        super(props);
        this.method = debounce(this.method.bind(this),1000);
    }
    method() { ... }
}

ES5

var SearchBox = React.createClass({
    method: function() {...},
    componentWillMount: function() {
       this.method = debounce(this.method.bind(this),100);
    },
});

ดูJsFiddle : 3 อินสแตนซ์กำลังสร้างรายการบันทึก 1 รายการต่ออินสแตนซ์ (ซึ่งสร้าง 3 รายการทั่วโลก)


ไม่ใช่ความคิดที่ดี:

var SearchBox = React.createClass({
  method: function() {...},
  debouncedMethod: debounce(this.method, 100);
});

มันจะไม่ทำงานเพราะในระหว่างการสร้างวัตถุคำอธิบายคลาสthisไม่ใช่วัตถุที่สร้างขึ้นเอง this.methodไม่ส่งคืนสิ่งที่คุณคาดหวังเนื่องจากthisบริบทไม่ใช่วัตถุเอง (ซึ่งจริง ๆ แล้วไม่มีจริง ๆ แล้วยัง BTW ตามที่เพิ่งถูกสร้างขึ้น)


ไม่ใช่ความคิดที่ดี:

var SearchBox = React.createClass({
  method: function() {...},
  debouncedMethod: function() {
      var debounced = debounce(this.method,100);
      debounced();
  },
});

คราวนี้คุณจะมีประสิทธิภาพการสร้างฟังก์ชั่นที่เรียก debounced this.methodของคุณ ปัญหาคือคุณกำลังสร้างมันขึ้นมาใหม่ทุกครั้งที่debouncedMethodฟังก์ชั่น debounce ที่สร้างขึ้นใหม่ไม่ทราบอะไรเกี่ยวกับการโทรในอดีต! คุณต้องนำฟังก์ชั่น debounce เดิมมาใช้ใหม่ตลอดเวลามิเช่นนั้นการเรียกคืนจะไม่เกิดขึ้น


ไม่ใช่ความคิดที่ดี:

var SearchBox = React.createClass({
  debouncedMethod: debounce(function () {...},100),
});

นี่มันยุ่งยากเล็กน้อยที่นี่

อินสแตนซ์ที่เมาท์ทั้งหมดของคลาสจะใช้ฟังก์ชัน debounce เดียวกันและส่วนใหญ่มักจะไม่ใช่สิ่งที่คุณต้องการ! ดูJsFiddle : 3 อินสแตนซ์กำลังสร้างรายการบันทึกเพียงรายการเดียวทั่วโลก

คุณต้องสร้างฟังก์ชั่นการลบล้างสำหรับแต่ละอินสแตนซ์ขององค์ประกอบและไม่ใช่ฟังก์ชั่นการหักครั้งเดียวในระดับชั้นซึ่งแบ่งใช้โดยแต่ละอินสแตนซ์ขององค์ประกอบ


ดูแลการรวมเหตุการณ์ของ React

สิ่งนี้เกี่ยวข้องกันเพราะเรามักจะต้องการ debounce หรือเค้นเหตุการณ์ DOM

ในการตอบสนองวัตถุเหตุการณ์ (เช่นSyntheticEvent) ที่คุณได้รับในการโทรกลับจะรวมกัน (ตอนนี้ได้รับการบันทึกแล้ว) ซึ่งหมายความว่าหลังจากเรียกการเรียกเหตุการณ์แล้ว SyntheticEvent ที่คุณได้รับจะถูกนำกลับไปไว้ในกลุ่มที่มีแอตทริบิวต์ว่างเพื่อลดแรงกดดัน GC

ดังนั้นหากคุณเข้าถึงSyntheticEventคุณสมบัติแบบอะซิงโครนัสกับการติดต่อกลับเดิม (เช่นในกรณีที่คุณเค้น / debounce) คุณสมบัติที่คุณเข้าถึงอาจถูกลบ หากคุณต้องการให้เหตุการณ์ไม่เคยถูกนำกลับมาใส่ในกลุ่มคุณสามารถใช้persist()วิธีการ

ไม่มีพฤติกรรม (พฤติกรรมเริ่มต้น: เหตุการณ์รวม)

onClick = e => {
  alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
  setTimeout(() => {
    alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
  }, 0);
};

ตัวที่ 2 (async) จะทำการพิมพ์hasNativeEvent=falseเนื่องจากคุณสมบัติของเหตุการณ์ถูกลบทิ้ง

ด้วยความคงอยู่

onClick = e => {
  e.persist();
  alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
  setTimeout(() => {
    alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
  }, 0);
};

ตัวที่ 2 (async) จะถูกพิมพ์hasNativeEvent=trueเพราะpersistช่วยให้คุณหลีกเลี่ยงการใส่เหตุการณ์กลับเข้าไปในกลุ่ม

คุณสามารถทดสอบ 2 พฤติกรรมเหล่านี้ได้ที่นี่: JsFiddle

อ่านคำตอบของ Julenสำหรับตัวอย่างการใช้persist()กับฟังก์ชั่น throttle / debounce


3
คำตอบที่ยอดเยี่ยมนี่คือการตั้งค่าที่ดีสำหรับการตั้งค่าเขตข้อมูลฟอร์มเป็น 'การโต้ตอบ' เป็นเวลาสองสามวินาทีหลังจากที่พวกเขาหยุดพิมพ์และจากนั้นสามารถยกเลิกการส่งแบบฟอร์มหรือ onBlur
arush_try.com

8
โปรดสังเกตว่าใน ES6 แทนที่จะกำหนดวิธีการของคุณภายในตัวสร้าง (รู้สึกแปลก) คุณสามารถทำhandleOnChange = debounce((e) => { /* onChange handler code here */ }, timeout)ในระดับสูงสุดของชั้นเรียนของคุณ คุณยังคงตั้งค่าอินสแตนซ์ของสมาชิกได้อย่างมีประสิทธิภาพ แต่ดูเหมือนว่าจะเป็นนิยามของวิธีปกติมากกว่าเล็กน้อย ไม่จำเป็นต้องมีconstructorถ้าคุณยังไม่ได้กำหนดไว้ ฉันคิดว่ามันเป็นสไตล์ที่ชอบ
thom_nic

24
อย่าลืมที่จะยกเลิกวิธีการ debounce ในcomponentWillUnmount: this.method.cancel()- มิฉะนั้นอาจต้องการ setState ในองค์ประกอบที่ไม่ได้ต่อเชื่อม
elado

4
@JonasKello คุณไม่สามารถ debounce ภายในคอมโพเนนต์ไร้สถานะเนื่องจากฟังก์ชั่น debounce นั้นเป็นของจริง คุณต้องการส่วนประกอบ stateful เพื่อเก็บฟังก์ชัน debounce นั้น แต่คุณสามารถเรียกคอมโพเนนต์ที่ไร้สัญชาติด้วยฟังก์ชัน debounce ได้ถ้าจำเป็น
เซบาสเตียน Lorber

2
เหตุใดคำตอบทั้งหมดจึงรวม _.debounce แทนที่จะเขียนฟังก์ชัน? มันต้องการไลบรารีทั้งหมดสำหรับฟังก์ชันนั้นหรือไม่?
chifliiiii

217

ส่วนประกอบที่ไม่ได้รับการควบคุม

คุณสามารถใช้วิธีการ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

แก้ไข: อัปเดตด้วย jsfiddle ที่ไม่ใช้ขีดล่างและใช้ debounce ธรรมดาของ javascript


สิ่งนี้ใช้ไม่ได้กับอินพุต เป้าหมายของเหตุการณ์ในฟังก์ชั่น debounce ไม่มีค่าอีกต่อไป ... ดังนั้นอินพุตยังคงว่างเปล่า
Etai

1
ซับซ้อนเล็กน้อยนี่ คุณต้องระมัดระวังเกี่ยวกับอุปกรณ์ประกอบฉาก หากคุณตั้งค่า<input value={this.props.someprop}...มันจะไม่แสดงผลอย่างถูกต้องเนื่องจากการอัปเดตบนแป้นกดไม่ได้ทำให้มันกลับเข้าไปในองค์ประกอบจนกระทั่งหลังจาก debounce ไม่เป็นไรvalue=ถ้าคุณมีความสุขที่จะไม่มีการจัดการ แต่ถ้าคุณต้องการเติมค่าล่วงหน้าและ / หรือผูกไว้ที่อื่นก็เห็นได้ชัดว่านี่ไม่ได้ผล
Alastair Maw

1
@Alastair คำถามที่พบบ่อยมีส่วนประกอบที่ไม่สามารถควบคุมได้นั่นเป็นสาเหตุที่คำตอบมีเช่นกัน ฉันได้เพิ่มรุ่นทางเลือกด้านล่างสำหรับส่วนประกอบที่ควบคุมโดยมีค่าที่เติมไว้ล่วงหน้า
julen

2
สิ่งนี้เป็นสิ่งที่อันตรายมากหากคุณติดตั้งส่วนประกอบหลาย ๆ ครั้งใน DOM โปรดดูstackoverflow.com/questions/23123138/…
Sebastien Lorber

4
ขณะนี้เป็นคำตอบที่ดีผมไม่แนะนำให้ใช้โดยเฉพาะอย่างยิ่งเมื่อมีจำนวนมากอาจจะเป็นของเหตุการณ์เช่นเดียวกับpersist mousemoveฉันได้เห็นรหัสกลายเป็นไม่ตอบสนองโดยสิ้นเชิง มันมีประสิทธิภาพมากขึ้นในการดึงข้อมูลที่ต้องการจากเหตุการณ์ดั้งเดิมในการเรียกใช้เหตุการณ์และจากนั้นเรียกใช้ฟังก์ชัน debounce / throttled ด้วยข้อมูลเท่านั้นไม่ใช่เหตุการณ์เอง ไม่จำเป็นต้องยืนยันเหตุการณ์ดังกล่าว
MrE

31

2019: ใช้ตะขอตอบกลับ 'useCallback'

หลังจากลองใช้วิธีการต่าง ๆ ฉันพบว่าการใช้useCallbackเป็นวิธีที่ง่ายและมีประสิทธิภาพมากที่สุดในการแก้ปัญหาการโทรหลายครั้งของการใช้debounceภายในonChangeเหตุการณ์

ตาม เอกสารตะขอ API ,

useCallback ส่งคืนเวอร์ชันที่จดจำไว้ของการเรียกกลับที่เปลี่ยนแปลงเฉพาะเมื่อการอ้างอิงอย่างใดอย่างหนึ่งเปลี่ยนไป

ผ่านอาร์เรย์ที่ว่างเปล่าเป็นการพึ่งพาทำให้แน่ใจว่าการโทรกลับถูกเรียกเพียงครั้งเดียว นี่คือการใช้งานที่เรียบง่าย:

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

หวังว่านี่จะช่วยได้!


3
ทางออกที่ยอดเยี่ยมถ้าคุณใช้ hooks คุณช่วยฉันหงุดหงิดอีกหลายชั่วโมง ขอบคุณ!
Carl Edwards

คุณช่วยอธิบายได้ไหมว่าทำไมการโทรหลายสายจึงเกิดขึ้นตั้งแต่แรก? ไม่debounce()ถือว่าการonChange()ติดต่อกลับเป็นวิธีการโทรกลับเดียวกันหรือไม่
El Anonimo

ฉันแก้ไขโซลูชันนี้เพื่อให้แอปทำงานในแอปของฉัน ครั้งแรกที่ฉันต้องย้ายสายconst testFunc2 = useCallback(debounce((text) => console.log('testFunc2() has ran:', text), 1000) , []);ภายในร่างกายขององค์ประกอบฟังก์ชั่นหรือทำปฏิกิริยาส่งข้อความข้อผิดพลาดเกี่ยวกับการใช้เบ็ดนอกมัน จากนั้นในการจัดการเหตุการณ์:onChange <input type='text' name='name' className='th-input-container__input' onChange={evt => {testFunc2(evt.target.value);}}
El Anonimo

นี่คือวิธีที่ฉันใช้โซลูชันนี้เพื่อให้ประเภทผู้ใช้ไปยังอินพุตจากนั้นส่งการเรียกใช้ API แบบย่อซึ่งมีค่าอินพุตเมื่อเขาพิมพ์เสร็จแล้ว stackoverflow.com/questions/59358092/… .
El Anonimo

14

ฉันพบโพสต์นี้โดย Justin Tulk มีประโยชน์มาก หลังจากความพยายามสองสามครั้งในสิ่งที่คนเราเห็นว่าเป็นวิธีที่เป็นทางการมากขึ้นด้วยปฏิกิริยา / การเปลี่ยนสีมันแสดงให้เห็นว่ามันล้มเหลวเนื่องจากเหตุการณ์สังเคราะห์ของรีแอค วิธีแก้ปัญหาของเขาจะใช้สถานะภายในบางอย่างเพื่อติดตามการเปลี่ยนแปลง / ป้อนค่าในอินพุตด้วยการโทรกลับทันทีหลังจากsetStateที่เรียกการกระทำ redux throttled / debounce ที่แสดงผลลัพธ์บางอย่างในเรียลไทม์

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

14

หากสิ่งที่คุณต้องการจากวัตถุเหตุการณ์คือการได้รับองค์ประกอบการป้อนข้อมูล DOM การแก้ปัญหานั้นง่ายกว่ามากเพียงใช้ refเพียงแค่ใช้ โปรดทราบว่าสิ่งนี้ต้องใช้ขีดล่าง :

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

2
defaultValue คือสิ่งที่ฉันต้องการ! ขอบคุณมากจักร :)
Tazo leladze

14

หลังจากดิ้นรนกับอินพุตข้อความชั่วขณะหนึ่งและไม่พบโซลูชันที่สมบูรณ์แบบด้วยตัวเองฉันพบสิ่งนี้ในเวลา 23.00 น.: ตอบโต้การตอบกลับการป้อนข้อมูลตอบสนอง-debounce

นี่คือตัวอย่างง่ายๆ:

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

ส่วนประกอบ DebounceInput ยอมรับอุปกรณ์ประกอบฉากทั้งหมดที่คุณสามารถกำหนดให้กับองค์ประกอบอินพุตปกติ ลองใช้codepen

ฉันหวังว่ามันจะช่วยคนอื่นด้วยและช่วยพวกเขาบ้าง


หลังจากลองวิธีแก้ปัญหาหลายอย่างที่แสดงไว้ที่นี่แน่นอนเป็นวิธีที่ง่ายที่สุด
Vadorequest

9

กับdebounceที่คุณต้องการให้เหตุการณ์สังเคราะห์เดิมไปรอบ ๆ event.persist()ด้วย React 16+นี่เป็นตัวอย่างการทำงานการทดสอบกับ

import React, { Component } from 'react';
import debounce from 'lodash/debounce'

class ItemType extends Component {

  evntHandler = debounce((e) => {
    console.log(e)
  }, 500);

  render() {
    return (
      <div className="form-field-wrap"
      onClick={e => {
        e.persist()
        this.evntHandler(e)
      }}>
        ...
      </div>
    );
  }
}
export default ItemType;

ด้วยองค์ประกอบที่ใช้งานได้คุณสามารถทำได้ -

const Search = ({ getBooks, query }) => {

  const handleOnSubmit = (e) => {
    e.preventDefault();
  }
  const debouncedGetBooks = debounce(query => {
    getBooks(query);
  }, 700);

  const onInputChange = e => {
    debouncedGetBooks(e.target.value)
  }

  return (
    <div className="search-books">
      <Form className="search-books--form" onSubmit={handleOnSubmit}>
        <Form.Group controlId="formBasicEmail">
          <Form.Control type="text" onChange={onInputChange} placeholder="Harry Potter" />
          <Form.Text className="text-muted">
            Search the world's most comprehensive index of full-text books.
          </Form.Text>
        </Form.Group>
        <Button variant="primary" type="submit">
          Search
        </Button>
      </Form>
    </div>
  )
}

ข้อมูลอ้างอิง - - https://gist.github.com/elijahmanor/08fc6c8468c994c844213e4a4344a709 - https://blog.revathskumar.com/2016/02/reactjs-using-debounce-in-react-components.html


1
การทำงานที่ดีที่สุดในการดำเนินงานที่ดีที่ฉันพบเพื่อให้ห่างไกล
วินเซนต์ถัง

8

หากคุณใช้ redux คุณสามารถทำได้อย่างหรูหราด้วยมิดเดิลแวร์ คุณสามารถกำหนดDebounceมิดเดิลแวร์เป็น:

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

จริงๆแล้วมีมิดเดิลแวร์ที่คุณสามารถใช้เพื่อทำสิ่งนี้เพื่อคุณ


ฉันคิดว่ามิดเดิลแวร์นี้ต้องเป็นอันแรกที่ถูกประหารชีวิตในapplyMiddleware(...)สายโซ่ถ้าเรามีจำนวนมาก
Youssef

การหมดเวลาไม่ได้เริ่มต้นและ clearTimeout แรกนั้นจะเกี่ยวข้องกับการไม่ได้กำหนดสำหรับพารามิเตอร์ ไม่ดี.
Jason Rice

7

ใช้ ES6 CLASS และตอบสนอง 15.xx & lodash.debounce ฉันใช้การอ้างอิงของ React ที่นี่เนื่องจากเหตุการณ์สูญเสียการเชื่อมโยงนี้ภายใน

class UserInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      userInput: ""
    };
    this.updateInput = _.debounce(this.updateInput, 500);
  }


  updateInput(userInput) {
    this.setState({
      userInput
    });
    //OrderActions.updateValue(userInput);//do some server stuff
  }


  render() {
    return ( <div>
      <p> User typed: {
        this.state.userInput
      } </p>
      <input ref = "userValue" onChange = {() => this.updateInput(this.refs.userValue.value) } type = "text" / >
      </div>
    );
  }
}

ReactDOM.render( <
  UserInput / > ,
  document.getElementById('root')
);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.5/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>


<div id="root"></div>


7

ข้อมูลที่ดีมากมายอยู่ที่นี่แล้ว แต่ต้องรวบรัด มันเหมาะกับฉัน ...

import React, {Component} from 'react';
import _ from 'lodash';

class MyComponent extends Component{
      constructor(props){
        super(props);
        this.handleChange = _.debounce(this.handleChange.bind(this),700);
      }; 

มันไม่ได้ผลสำหรับฉัน สถานะไม่อัปเดต ถ้าฉันเอา_debounce wrapper มันใช้งานได้ ฉันชอบความคิดนี้!
โมท Zart

ฉันต้องเห็นรหัสของคุณที่นี่ แต่ฉันคิดว่าจะมีบางอย่างเกิดขึ้น ... หวังว่าคำตอบที่ละเอียดกว่านี้จะทำให้เกิดแสงสว่าง stackoverflow.com/questions/23123138/…
chad steele

6

คุณสามารถใช้วิธีการหักล้าง 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);
}

คุณสามารถยกเลิกวิธีการ debounce โดยใช้วิธีการด้านล่าง

this.debounceHandleUpdate.cancel();

หวังว่ามันจะช่วยคุณ ไชโย !!


5

FYI

นี่คือการใช้งาน PoC อื่น:

  • ไม่มีห้องสมุดใด ๆ (เช่น lodash) สำหรับการเปิดใช้งาน
  • ใช้ React Hooks API

ฉันหวังว่ามันจะช่วย :)

import React, { useState, useEffect, ChangeEvent } from 'react';

export default function DebouncedSearchBox({
  inputType,
  handleSearch,
  placeholder,
  debounceInterval,
}: {
  inputType?: string;
  handleSearch: (q: string) => void;
  placeholder: string;
  debounceInterval: number;
}) {
  const [query, setQuery] = useState<string>('');
  const [timer, setTimer] = useState<NodeJS.Timer | undefined>();

  useEffect(() => {
    if (timer) {
      clearTimeout(timer);
    }
    setTimer(setTimeout(() => {
      handleSearch(query);
    }, debounceInterval));
  }, [query]);

  const handleOnChange = (e: ChangeEvent<HTMLInputElement>): void => {
    setQuery(e.target.value);
  };

  return (
    <input
      type={inputType || 'text'}
      className="form-control"
      placeholder={placeholder}
      value={query}
      onChange={handleOnChange}
    />
  );
}

4

มีuse-debounceแพ็คเกจที่คุณสามารถใช้กับ ReactJS hooks ได้

จากแพคเกจ README:

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

ดังที่คุณเห็นจากตัวอย่างด้านบนมันถูกตั้งค่าให้อัพเดตตัวแปรvalueเพียงครั้งเดียวทุกวินาที (1,000 มิลลิวินาที)


3

เพียงแค่อีกหนึ่งตัวแปรที่มีการตอบสนองล่าสุดและ 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)} />)
  }
}


3

คุณลอง?

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

2

แทนที่จะห่อ handleOnChange ใน debounce () ทำไมไม่ตัดการโทร ajax ภายในฟังก์ชั่นการโทรกลับภายใน debounce ดังนั้นจะไม่ทำลายวัตถุเหตุการณ์ ดังนั้นสิ่งนี้:

handleOnChange: function (event) {
   debounce(
     $.ajax({})
  , 250);
}

4
เนื่องจากวัตถุเหตุการณ์ไม่เปลี่ยนรูปไม่ได้และถูกทำลายโดย ReactJS ดังนั้นแม้ว่าคุณจะทำการปิดและจับภาพการปิดรหัสจึงจะล้มเหลว
Henrik

2

นี่คือตัวอย่างที่ฉันสร้างขึ้นด้วยการตัดคลาสอีกหนึ่งอันด้วยดีบับ สิ่งนี้ทำให้ตัวเองมีความสวยงามยิ่งขึ้นในการเป็นฟังก์ชั่นการสั่งซื้อมัณฑนากร / สูงกว่า:

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

2

ขณะนี้มีโซลูชันอื่นสำหรับ React และ React Native ในปลายปี 2019 :

ตอบสนอง-debounce องค์ประกอบ

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

มันเป็นส่วนประกอบใช้งานง่ายเล็ก ๆ และ Widley รองรับ

ตัวอย่าง:

ป้อนคำอธิบายรูปภาพที่นี่

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;

* ฉันเป็นผู้สร้างองค์ประกอบนี้


1

ฉันค้นหาวิธีแก้ไขปัญหาเดียวกันและพบกับเธรดนี้รวมถึงคนอื่น ๆ แต่พวกเขามีปัญหาเดียวกัน: หากคุณพยายามทำhandleOnChangeหน้าที่และคุณต้องการค่าจากเป้าหมายเหตุการณ์คุณจะได้รับcannot read property value of nullหรือบางส่วน ข้อผิดพลาดดังกล่าว ในกรณีของฉันฉันจำเป็นต้องรักษาบริบทthisภายในของฟังก์ชัน debounce เนื่องจากฉันกำลังดำเนินการ fluxible นี่เป็นวิธีแก้ปัญหาของฉันมันใช้งานได้ดีสำหรับกรณีการใช้งานของฉันดังนั้นฉันจะทิ้งไว้ที่นี่ในกรณีที่มีผู้อื่นเข้ามาที่หัวข้อนี้:

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

1

สำหรับthrottleหรือdebounceวิธีที่ดีที่สุดคือการสร้างฟังก์ชั่นผู้สร้างเพื่อให้คุณสามารถใช้งานได้ทุกที่ตัวอย่างเช่น:

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

เหตุผลที่สิ่งนี้ไม่ทำงานเพราะจะสร้างฟังก์ชั่นเค้นใหม่ทุกครั้งที่มีเหตุการณ์ที่เรียกว่าแทนที่จะใช้ฟังก์ชั่นเค้นที่เหมือนกันดังนั้นโดยทั่วไปแล้วเค้นจะไม่มีประโยชน์;)

นอกจากนี้ถ้าคุณใช้debounceหรือthrottleคุณไม่ต้องการsetTimeoutหรือclearTimeoutนี่คือเหตุผลที่เราใช้มัน: P


1

นี่คือตัวอย่างข้อมูลโดยใช้วิธีการของ @ Abra ที่ห่อหุ้มไว้ในฟังก์ชั่นส่วนประกอบ (เราใช้เนื้อผ้าสำหรับ UI เพียงแค่แทนที่ด้วยปุ่มง่าย ๆ )

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

1

โซลูชันของฉันใช้ตะขอ (เขียนใน typescript)

ฉันมีตะขอหลัก 2 อันuseDebouncedValueและuseDebouncedCallback

ก่อน - useDebouncedValue

สมมติว่าเรามีช่องค้นหา แต่เราต้องการถามเซิร์ฟเวอร์เพื่อค้นหาผลลัพธ์หลังจากผู้ใช้หยุดพิมพ์เป็น 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)} />
}

การดำเนินงาน

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

ที่สอง useDebouncedCallback

มันเพิ่งสร้างฟังก์ชั่น 'debounce' ในขอบเขตขององค์ประกอบของคุณ

สมมติว่าเรามีส่วนประกอบพร้อมปุ่มที่จะแสดงการเตือน 500 มิลลิวินาทีหลังจากที่คุณหยุดการคลิก

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

  const debouncedShowAlert = useDebouncedCallback(showAlert, 500);

  return <button onClick={debouncedShowAlert}>Click</button>
}

การใช้งาน (โปรดทราบฉันกำลังใช้ lodash / debounce เป็นผู้ช่วย)

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

ต่อไปนี้เป็นตัวอย่าง TypeScript ที่ใช้งานได้สำหรับผู้ที่ใช้ TS และต้องการลบล้างasyncฟังก์ชัน

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

สายนี้เล็กน้อย แต่สิ่งนี้จะช่วยได้ สร้างคลาสนี้ (เขียนใน typescript แต่ง่ายต่อการแปลงเป็นจาวาสคริปต์)

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

0

คุณสามารถใช้ tlence tlence

function log(server) {
  console.log('connecting to', server);
}

const debounceLog = debounce(log, 5000);
// just run last call to 5s
debounceLog('local');
debounceLog('local');
debounceLog('local');
debounceLog('local');
debounceLog('local');
debounceLog('local');

0

วิธีการแก้ปัญหาของ Julen นั้นยากต่อการอ่านนี่คือรหัสการตอบสนองที่ชัดเจนและตรงประเด็นสำหรับทุกคนที่ทำให้เขาสะดุดตามชื่อและไม่ใช่รายละเอียดเล็ก ๆ ของคำถาม

tl; dr version : เมื่อคุณอัปเดตผู้สังเกตการณ์ให้ส่งวิธีเรียกกำหนดการแทนและในทางกลับกันจะแจ้งให้ผู้สังเกตการณ์ทราบจริง (หรือแสดง 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)
        }
    },

});

3
สิ่งนี้จะไม่ทำให้สถานะ / เวลาของการดีบั๊กทั่วโลกในทุกอินสแตนซ์ของ InputField เพราะมันถูกสร้างขึ้นด้วยคำจำกัดความของชั้นเรียน? อาจเป็นสิ่งที่คุณต้องการ แต่มันก็คุ้มค่าที่จะสังเกต
robbles

1
อันตรายหากติดตั้งหลายครั้งในโดมตรวจสอบstackoverflow.com/questions/23123138/ …
Sebastien Lorber

2
นี่เป็นวิธีแก้ปัญหาที่ไม่ดีเนื่องจากปัญหาการเมาท์สองครั้ง - คุณกำลังกำหนดให้ฟังก์ชันของคุณเปลี่ยนเป็นซิงเกิลตันและนั่นไม่ใช่ความคิดที่ดี -1
Henrik

0

หลีกเลี่ยงการใช้event.persist()- คุณต้องการให้ React รีไซเคิลเหตุการณ์สังเคราะห์ ฉันคิดว่าวิธีที่สะอาดที่สุดไม่ว่าคุณจะใช้คลาสหรือ hooks คือแบ่งการโทรกลับออกเป็นสองส่วน:

  1. การโทรกลับโดยไม่มีการโต้แย้ง
  2. เรียกฟังก์ชั่น debounced มีเพียงชิ้นส่วนของเหตุการณ์ที่คุณต้องการ (ดังนั้นเหตุการณ์สังเคราะห์สามารถนำมารีไซเคิล)

การเรียนการสอน

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และส่งผ่านการอ้างอิงเป็นอย่างอื่นคุณจะทำงานกับข้อมูลเก่า (ไม่ใช้กับคลาสของหลักสูตร)


0

ขยายการใช้สถานะตะขอ

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/


0

พบปัญหานี้วันนี้ แก้ไขได้โดยใช้และsetTimeoutclearTimeout

ฉันจะยกตัวอย่างที่คุณสามารถปรับได้:

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

คุณสามารถ refactor ในองค์ประกอบของฟังก์ชั่นของมันเอง:

import React from 'react'

function DebouncedInput({ debounceTime, callback}) {
  let debounceTimer = null
  return (
    <input type="text" onChange={(event) => {
      clearTimeout(debounceTimer);

      debounceTimer = setTimeout(
        callback.bind(null, event.target.value), 
        debounceTime
      )
    }} />
  )
}

export default DebouncedInput

และใช้มันเช่น:

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.