React.js में बहस करें


496

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

मैं आपके साथ एक ही समस्या से मिला था, नीचे शानदार जवाब the लेकिन मुझे लगता है कि आपने गलत तरीके का इस्तेमाल किया debounce। यहाँ, जब onChange={debounce(this.handleOnChange, 200)}/>, यह debounce functionहर बार आह्वान करेगा । लेकिन, वास्तव में, हमें जिस चीज की आवश्यकता होती है, वह फ़ंक्शन को इनवॉइस करता है, जो डेब्यू फ़ंक्शन को वापस करता है।
पिंगफैफेफी

जवाबों:


834

२०१ ९: हुक + वाद-विवाद का प्रयास करें

यह अब तक का सबसे संस्करण है कि मैं इस समस्या को कैसे हल करूंगा। मै इस्तेमाल करूंगा:

यह कुछ प्रारंभिक वायरिंग है लेकिन आप अपने दम पर आदिम ब्लॉकों की रचना कर रहे हैं, और आप अपने स्वयं के कस्टम हुक बना सकते हैं ताकि आपको केवल एक बार ऐसा करने की आवश्यकता हो।

// 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- हुक प्रलेखन पढ़ना चाहिए ।


2018: वाद-विवाद वाद-विवाद का प्रयास करें

हम अक्सर बेकार अनुरोधों के साथ बैकएंड को बाढ़ से बचने के लिए एपीआई कॉल की बहस करना चाहते हैं।

2018 में, कॉलबैक (लॉडश / अंडरस्कोर) के साथ काम करना मुझे बुरा और त्रुटि-रहित लगता है। एक अनियंत्रित क्रम में एपीआई कॉल का समाधान होने के कारण बॉयलरप्लेट और कॉन्सिक्वेंसी मुद्दों का सामना करना आसान है।

मैंने आपके दर्द को हल करने के लिए रिएक्ट के साथ एक छोटी सी लाइब्रेरी बनाई है: भयानक-बहस-वादा

इससे अधिक जटिल नहीं होना चाहिए:

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

विवादित फ़ंक्शन यह सुनिश्चित करता है कि:

  • एपीआई कॉल पर बहस की जाएगी
  • विवादास्पद फ़ंक्शन हमेशा एक वादा लौटाता है
  • केवल अंतिम कॉल का दिया गया वादा हल होगा
  • एक this.setState({ result });एपीआई प्रति कॉल के साथ होगा

अंततः, यदि आपका घटक अनमाउंट करता है, तो आप एक और ट्रिक जोड़ सकते हैं:

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

ध्यान दें कि observables / सही ढंग से उपयोग करें (RxJS) भी आदानों debouncing के लिए बिल्कुल उपयुक्त हो सकता है, लेकिन यह एक अधिक शक्तिशाली अमूर्त जो कठिन जानने के लिए किया जा सकता है है।


<2017: अभी भी कॉलबैक डिबॉन्डिंग का उपयोग करना चाहते हैं?

यहाँ महत्वपूर्ण हिस्सा प्रति घटक उदाहरण के लिए एक एकल डेबिट (या थ्रॉटल) फ़ंक्शन बनाना है । आप हर बार डेबिट (या थ्रोटल) फ़ंक्शन को फिर से बनाना नहीं चाहते हैं, और आप एक ही डेबिट फ़ंक्शन को साझा करने के लिए या तो कई उदाहरण नहीं चाहते हैं।

मैं इस उत्तर में एक डिबगिंग फ़ंक्शन को परिभाषित नहीं कर रहा हूं क्योंकि यह वास्तव में प्रासंगिक नहीं है, लेकिन यह उत्तर _.debounceअंडरस्कोर या लॉश के साथ-साथ किसी भी उपयोगकर्ता द्वारा प्रदान किए गए डिबेंचिंग फ़ंक्शन के साथ पूरी तरह से ठीक काम करेगा ।


अच्छा विचार:

चूँकि विवादास्पद फ़ंक्शन स्टेटफुल हैं, हमें प्रति घटक उदाहरण में एक डिबेंक्टेड फंक्शन बनाना होगा ।

ईएस 6 (वर्ग संपत्ति) : अनुशंसित

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

ईएस 6 (क्लास कंस्ट्रक्टर)

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संदर्भ स्वयं वस्तु नहीं है (जो वास्तव में वास्तव में अभी तक मौजूद नहीं है, क्योंकि यह अभी बनाया जा रहा है)।


अच्छा विचार नहीं:

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

इस बार आप प्रभावी ढंग से एक विवादास्पद फ़ंक्शन बना रहे हैं जो आपकी कॉल करता है this.method। समस्या यह है कि आप इसे हर debouncedMethodकॉल पर फिर से बना रहे हैं , इसलिए नए बनाए गए डेब्यू फ़ंक्शन को पूर्व कॉल के बारे में कुछ भी नहीं पता है! आपको समय के साथ एक ही डेबिट किए गए फ़ंक्शन का पुन: उपयोग करना होगा या डेबिटिंग नहीं होगी।


अच्छा विचार नहीं:

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

यह यहाँ थोड़ा मुश्किल है।

वर्ग के सभी माउंटेड उदाहरण एक ही बहस वाले फंक्शन को साझा करेंगे, और अधिकतर यह वही नहीं है जो आप चाहते हैं! JsFiddle देखें : 3 उदाहरण विश्व स्तर पर केवल 1 लॉग प्रविष्टि का निर्माण कर रहे हैं।

आपको प्रत्येक घटक उदाहरण के लिए एक डेबिट फ़ंक्शन बनाना होगा , और प्रत्येक घटक आवृत्ति द्वारा साझा की गई क्लास स्तर पर एक भी डेबिट फ़ंक्शन नहीं है।


रिएक्ट के इवेंट पूलिंग का ध्यान रखें

यह संबंधित है क्योंकि हम अक्सर डोम घटनाओं की चर्चा करना या गला घोंटना चाहते हैं।

प्रतिक्रिया में, SyntheticEventकॉलबैक में प्राप्त होने वाली ईवेंट ऑब्जेक्ट्स (यानी ) को पूल किया जाता है (यह अब प्रलेखित है )। इसका मतलब यह है कि ईवेंट कॉलबैक कहे जाने के बाद, आपके द्वारा प्राप्त SyntheticEvent को GC प्रेशर को कम करने के लिए खाली विशेषताओं के साथ पूल में वापस रखा जाएगा।

इसलिए यदि आप SyntheticEventमूल कॉलबैक के लिए अतुल्यकालिक रूप से गुणों का उपयोग करते हैं (जैसा कि यदि आप थ्रॉटल / डेब्यू करते हैं तो मामला हो सकता है), आपके द्वारा उपयोग किए जाने वाले गुण मिटाए जा सकते हैं। यदि आप चाहते हैं कि घटना को कभी भी पूल में वापस न रखा जाए, तो आप इसका उपयोग कर सकते हैं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);
};

दूसरा (async) प्रिंट करेगा hasNativeEvent=trueक्योंकि persistआपको इवेंट को पूल में वापस रखने से बचने की अनुमति देता है।

आप इन 2 व्यवहारों का परीक्षण यहां कर सकते हैं: JsFiddle

एक थ्रॉटल / डिब्यूशन फ़ंक्शन के साथ उपयोग करने के उदाहरण के लिए जूलेन का जवाब पढ़ें persist()


3
शानदार उत्तर, यह टाइप
फ़ील्ड स्टेट्स

8
ध्यान दें कि ES6 में, अपने तरीके को कंस्ट्रक्टर के अंदर परिभाषित करने के बजाय (अजीब लगता है) आप handleOnChange = debounce((e) => { /* onChange handler code here */ }, timeout)अपनी कक्षा के शीर्ष स्तर पर कर सकते हैं । आप अभी भी प्रभावी रूप से एक इंस्टा सदस्य सेट कर रहे हैं, लेकिन यह सामान्य पद्धति की परिभाषा की तरह थोड़ा अधिक है। constructorअगर आपको पहले से कोई परिभाषित नहीं है, तो इसकी कोई आवश्यकता नहीं है। मुझे लगता है कि यह ज्यादातर शैली की प्राथमिकता है।
thom_nic

24
इसमें दिए गए विवादित तरीके को रद्द करना न भूलें componentWillUnmount: this.method.cancel()- अन्यथा यह अनमाउंट किए गए घटक पर सेट करना चाह सकता है।
२१:११

4
@JonasKello आप एक स्टेटलेस कंपोनेंट के अंदर डेब्यू नहीं कर सकते क्योंकि डिबेटेड फंक्शन वास्तव में स्टेटफुल है। आपको उस डेबिट फ़ंक्शन को होल्ड करने के लिए एक स्टेटफुल कंपोनेंट की आवश्यकता होती है, लेकिन ज़रूरत पड़ने पर आप पहले से ही फंक्शन वाले स्टेटलेस कंपोनेंट को कॉल कर सकते हैं।
सेबस्टियन लॉबर

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 0.12 प्रतिक्रिया के लिए

संपादित करें: सेबस्टियन लॉबर द्वारा उठाए गए मुद्दे को संबोधित करने के लिए अद्यतन उदाहरण

संपादित करें: jsfiddle के साथ अपडेट किया गया जो अंडरस्कोर का उपयोग नहीं करता है और सादे जावास्क्रिप्ट डिसकाउंट का उपयोग करता है।


यह इनपुट्स के लिए काम नहीं करता है। विवादास्पद फ़ंक्शन में इवेंट लक्ष्य का अब कोई मूल्य नहीं है ... इसलिए इनपुट खाली रहता है।
एताई

1
थोड़ा जटिल, यह। आपको प्रॉप्स के बारे में थोड़ा सावधान रहना होगा। यदि आप सेट करते हैं, <input value={this.props.someprop}...तो यह ठीक से रेंडर नहीं होगा क्योंकि कीपर पर अपडेट डिब्यू के बाद तक इसे कंपोनेंट में वापस नहीं लाता है। value=यदि आप इसके अप्रबंधित होने के लिए खुश हैं , तो इसे छोड़ना ठीक है , लेकिन यदि आप मूल्य को पूर्व-आबाद करना चाहते हैं और / या इसे कहीं और बांधते हैं, तो जाहिर है कि यह काम नहीं करता है।
एलेस्टेयर मोव

1
@AlastairMaw प्रश्न में एक अनियंत्रित घटक था, इसीलिए इसका उत्तर भी है। मैंने नियंत्रित घटकों के लिए एक वैकल्पिक संस्करण के नीचे जोड़ा है, पूर्व-आबादी वाले मूल्य के साथ।
जुलेन

2
यह बहुत खतरनाक है यदि आप DOM में कंपोनेंट म्यूटेंट बार माउंट करते हैं, तो देखें stackoverflow.com/questions/23123138/…
सेबस्टियन लॉर्बर

4
हालांकि यह एक महान जवाब है, मैं persistविशेष रूप से उपयोग करने की सलाह नहीं देता हूं जब बहुत सारी घटनाएं हो सकती हैं, जैसे कि mousemove। मैंने देखा है कि कोड इस तरह पूरी तरह से अनुत्तरदायी हो गया है। ईवेंट कॉल में मूल घटना से आवश्यक डेटा निकालने के लिए यह बहुत अधिक कुशल है, और फिर केवल डेटा के साथ डेबिट / थ्रॉटल फ़ंक्शन को कॉल करें, न कि इवेंट को ही। इस तरह से घटना को जारी रखने की जरूरत नहीं है
MrE

31

2019: 'यूज़कॉलबैक' प्रतिक्रिया हुक का उपयोग करें

कई अलग-अलग तरीकों की कोशिश करने के बाद, मैंने एक घटना के भीतर useCallbackउपयोग की कई कॉल समस्या को हल करने में सबसे सरल और सबसे कुशल होने का उपयोग किया।debounceonChange

के अनुसार हुक 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
यदि आप हुक का उपयोग कर रहे हैं तो उत्कृष्ट समाधान। आपने मुझे कई घंटों की हताशा से बचाया। धन्यवाद!
कार्ल एडवर्ड्स

क्या आप यह बता सकते हैं कि पहली बार में कई कॉल क्यों होते हैं? क्या कॉलबैक को समान कॉलबैक विधि debounce()नहीं माना जाता है onChange()?
एल अन्निमो

मैंने अपने ऐप में काम करने के लिए इस समाधान को संशोधित किया। पहले मुझे 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);}}:।
एल एनोनिमो

यहां बताया गया है कि मैंने इस समाधान का उपयोग उपयोगकर्ता को इनपुट के लिए टाइप करने के लिए किया था, फिर टाइप किए जाने पर इनपुट मूल्य के साथ एक डेबिट एपीआई कॉल भेजें। stackoverflow.com/questions/59358092/…
एल Anonimo

14

मुझे जस्टिन टुलक की यह पोस्ट बहुत मददगार लगी। कुछ प्रयासों के बाद, प्रतिक्रिया / रिडक्स के साथ और अधिक आधिकारिक तरीका क्या होगा, यह दर्शाता है कि यह रिएक्ट के सिंथेटिक इवेंट पूलिंग के कारण विफल रहता है । उसका समाधान तब इनपुट में परिवर्तित / दर्ज किए गए मान को ट्रैक करने के लिए कुछ आंतरिक स्थिति का उपयोग करता है, एक कॉलबैक के साथ setStateजिसके बाद एक थ्रॉटल / विचाराधीन Redux कार्रवाई कहता है जो वास्तविक समय में कुछ परिणाम दिखाता है।

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

यदि आपको ईवेंट इनपुट ऑब्जेक्ट प्राप्त करने के लिए इवेंट ऑब्जेक्ट की आवश्यकता है, तो समाधान बहुत सरल है - बस उपयोग करें 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

थोड़ी देर के लिए पाठ इनपुट के साथ संघर्ष करने और अपने दम पर एक सही समाधान नहीं ढूंढने के बाद, मुझे यह npm पर मिला: प्रतिक्रिया-डेबिट-इनपुट

ये रहा एक सरल उदाहरण:

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 घटक उन सभी प्रॉप्स को स्वीकार करता है जिन्हें आप एक सामान्य इनपुट तत्व को असाइन कर सकते हैं। कोडपेन पर इसे आज़माएं

मुझे उम्मीद है कि यह किसी और को भी मदद करता है और उन्हें कुछ समय बचाता है।


यहाँ सूचीबद्ध कई समाधानों को आज़माने के बाद, निश्चित रूप से सबसे आसान था।

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/reajjs-using-debounce-in-react-compenders.html


1
महान सर्वोत्तम कार्यान्वयन मैं अब तक पाया
विन्सेन्ट तांग

8

यदि आप रिडक्स का उपयोग कर रहे हैं तो आप इसे मिडिलवेयर के साथ बहुत ही सुरुचिपूर्ण तरीके से कर सकते हैं। आप एक 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}
}

वास्तव में पहले से ही मिडलवेयर है जो आपके लिए ऐसा करने के लिए npm से दूर हो सकता है।


मुझे लगता है कि इस मिडलवेयर को applyMiddleware(...)श्रृंखला में सबसे पहले होना चाहिए अगर हमारे पास कई हैं
Youssef

टाइमआउट को प्रारंभ नहीं किया गया है और यह कि पहले क्लियर टाइमआउट एक परम के लिए अपरिभाषित होगा। अच्छा नही।
जेसन राइस

7

ES6 वर्ग का उपयोग करना और प्रतिक्रिया 15.xx प्रतिक्रिया का उपयोग और lodash.debounce इम refs यहाँ घटना घाटा इस बाँध आंतरिक रूप से के बाद से।

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 आवरण हटाता हूं तो यह काम करता है। मैं हालांकि इस विचार से प्यार करता हूँ!
मोर्ट ज़ार्ट

मुझे आपका कोड यहाँ बहुत कुछ देखने के लिए देखना होगा, लेकिन मुझे संदेह है कि वहाँ कुछ और चल रहा है ... उम्मीद है कि यह और अधिक गहन उत्तर कुछ प्रकाश डालेगा। stackoverflow.com/questions/23123138/…
चाड स्टील

6

आप लॉडश वाद-विवाद 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();

आशा है कि यह आपकी मदद करता है। चीयर्स !!


5

FYI करें

यहाँ एक और PoC कार्यान्वयन है:

  • डिबगिंग के लिए किसी भी पुस्तकालय (जैसे दर्ज) के बिना
  • रिएक्ट हुक एपीआई का उपयोग करना

मुझे उम्मीद है यह मदद करेगा :)

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 हुक के साथ उपयोग कर सकते हैं।

पैकेज की 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हर दूसरे (1000 मिलीसेकंड) केवल एक बार अपडेट करने के लिए सेट किया गया है ।


3

हाल ही में प्रतिक्रिया और लॉश के साथ एक और संस्करण।

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: function (event) {
   debounce(
     $.ajax({})
  , 250);
}

4
क्योंकि ईवेंट ऑब्जेक्ट अपरिवर्तनीय नहीं है और ReactJS द्वारा नष्ट हो जाता है, इसलिए भले ही आप लपेटें और क्लोजर कैप्चर प्राप्त करें, कोड विफल हो जाएगा।
हेनरिक

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

अब देर से / 2019 में प्रतिक्रिया और प्रतिक्रिया मूल के लिए एक और समाधान है :

प्रतिक्रिया-debounce घटक

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

* मैं इस घटक का निर्माता हूं


1

मैं उसी समस्या के समाधान की खोज कर रहा था और इस धागे के साथ-साथ कुछ अन्य लोगों के पास भी आया, लेकिन उनके पास भी यही समस्या थी: यदि आप एक handleOnChangeफ़ंक्शन करने की कोशिश कर रहे हैं और आपको इवेंट लक्ष्य से मूल्य की आवश्यकता है, तो आपको मिलेगा cannot read property value of nullया कुछ ऐसी त्रुटि। मेरे मामले में, मुझे thisएक फाल्सीबल एक्शन को अंजाम देने के बाद से बहस के फंक्शन के अंदर के संदर्भ को संरक्षित करने की जरूरत है । यहाँ मेरा समाधान है, यह मेरे उपयोग के मामले के लिए अच्छी तरह से काम करता है इसलिए मैं इसे यहाँ छोड़ रहा हूँ अगर कोई इस धागे के पार आता है:

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

मेरा समाधान हुक आधारित (टाइपस्क्रिप्ट में लिखा गया है)।

मुझे 2 मुख्य हुक मिले हैं useDebouncedValueऔरuseDebouncedCallback

प्रथम - useDebouncedValue

मान लें कि हमें एक खोज बॉक्स मिल गया है, लेकिन हम उपयोगकर्ता को 0,5 के लिए लिखना बंद करने के बाद खोज परिणामों के लिए सर्वर से पूछना चाहते हैं

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

यह सिर्फ आपके घटक के दायरे में एक 'बहस' समारोह बनाता है।

मान लें कि हमें एक बटन के साथ एक घटक मिला है जो आपको क्लिक करने से रोकने के बाद अलर्ट 500ms दिखाएगा।

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

0

यहां उन लोगों के लिए एक वर्किंग टाइपस्क्रिप्ट उदाहरण दिया गया है जो टीएस का उपयोग करते हैं और 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

थोड़ी देर यहाँ लेकिन यह मदद करनी चाहिए। यह वर्ग बनाएं (टाइपस्क्रिप्ट में लिखा गया है, लेकिन इसे जावास्क्रिप्ट में बदलना आसान है)

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

जूलन समाधान पढ़ने में कठिन है, यहां किसी के लिए भी स्पष्ट और टू-द-पॉइंट रिएक्शन कोड है, जो शीर्षक के आधार पर उसे ठोकर मारता है न कि सवाल का छोटा विवरण।

tl; डॉ संस्करण : जब आप पर्यवेक्षकों को अपडेट करेंगे तो इसके बजाय एक शेड्यूल विधि भेजें और बदले में वास्तव में पर्यवेक्षकों को सूचित करेंगे (या ajax, आदि प्रदर्शन करेंगे)

उदाहरण घटक 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
क्या यह इनपुटफ़िल्ड के सभी उदाहरणों में बहस के राज्य / समय को वैश्विक नहीं बना देगा, क्योंकि यह वर्ग परिभाषा के साथ बनाया गया है? शायद यही आप चाहते हैं, लेकिन यह ध्यान देने योग्य है।
डकैतों

1
खतरनाक अगर डोम में कई बार चढ़े, तो stackoverflow.com/questions/23123138/…
सेबस्टियन लॉर्बर

2
यह एक बुरा समाधान है, क्योंकि डबल-माउंट मुद्दों के कारण - आप एक सिंगलटन को शेड्यूल करने के लिए अपना कार्य कर रहे हैं और यह एक अच्छा विचार नहीं है। -1
हेनरिक

0

उपयोग करने से बचें event.persist()- आप रिएक्ट को सिंथेटिक इवेंट को रीसायकल करने देना चाहते हैं। मुझे लगता है कि आप कक्षाओं या हुक का उपयोग करने का सबसे साफ तरीका है, कॉलबैक को दो टुकड़ों में विभाजित करना है:

  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और उन पर निर्भरता के रूप में पास करना चाहिए अन्यथा आप बासी डेटा के साथ काम करेंगे (पाठ्यक्रम की कक्षाओं पर लागू नहीं होते हैं)।


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

आज इस समस्या का सामना करें। इसका उपयोग कर हल किया setTimeoutऔर clearTimeout

मैं एक उदाहरण दूंगा जिसे आप अनुकूलित कर सकते हैं:

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 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.