React でデバウンスを実行するにはどうすればいいですか?
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
}
});
ベストアンサー1
2019: フックとプロミスのデバウンスを試す
これは私がこの問題を解決する方法の最新バージョンです。私は以下を使用します:
- 素晴らしいデバウンスプロミス非同期関数をデバウンスする
- 定数を使用するデバウンスされた関数をコンポーネントに保存する
- 反応非同期フック結果をコンポーネントに取り込む
これは初期の配線ですが、プリミティブ ブロックを独自に構成しており、独自のカスタム フックを作成できるため、これを 1 回だけ実行すれば済みます。
// 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>
);
};
この例を実行するとここそして、あなたは反応非同期フック詳細についてはドキュメントをご覧ください。
2018: プロミスのデバウンスを試す
多くの場合、無駄なリクエストがバックエンドに殺到するのを避けるために、API 呼び出しをデバウンスする必要があります。
2018年、コールバック(ロダシュ/アンダースコア) は、私にとっては、良くなく、エラーが発生しやすいと感じます。 API 呼び出しが任意の順序で解決されるため、定型句や同時実行の問題に遭遇しやすくなります。
あなたの悩みを解決するために、React を念頭に置いた小さなライブラリを作成しました:素晴らしいデバウンスプロミス。
これ以上複雑になることはありません。
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 });
};
}
デバウンス関数により、次のことが保証されます。
- API呼び出しはデバウンスされます
- デバウンスされた関数は常にpromiseを返す
- 最後の呼び出しで返されたpromiseのみが解決されます
this.setState({ result });
API呼び出しごとに1回発生します
最終的には、コンポーネントがアンマウントされる場合に別のトリックを追加できます。
componentWillUnmount() {
this.setState = () => {};
}
Observables (翻訳) も入力のデバウンスに最適ですが、より強力な抽象化であるため、正しく学習/使用するのが難しい場合があります。
< 2017: コールバック デバウンスをまだ使用したいですか?
ここで重要なのは、コンポーネント インスタンスごとに 1 つのデバウンス (またはスロットル) 関数を作成することです。デバウンス (またはスロットル) 関数を毎回再作成したり、複数のインスタンスで同じデバウンス関数を共有したりすることは望ましくありません。
_.debounce
この回答では、あまり関係がないのでデバウンス関数を定義していませんが、この回答はUnderscore.js や Lodash、およびユーザーが提供するデバウンス関数でも問題なく動作します。
良いアイデア:
デバウンスされた関数はステートフルであるため、コンポーネント インスタンスごとに 1 つのデバウンスされた関数を作成する必要があります。
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() { ... }
}
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 つのログ エントリのみを生成しています。
各コンポーネント インスタンスごとにデバウンスされた関数を作成する必要があります。各コンポーネント インスタンスで共有されるクラス レベルの単一のデバウンスされた関数を作成する必要はありません。
Reactのイベントプーリングに注意する
これは、DOM イベントをデバウンスまたはスロットルする必要があることが多いため関連しています。
SyntheticEvent
Reactでは、コールバックで受け取ったイベントオブジェクト(つまり)はプールされます(これは現在文書化されたつまり、イベントコールバックが呼び出された後、受け取ったSyntheticEventは空の属性でプールに戻され、GCプレッシャー。
したがって、SyntheticEvent
元のコールバックに対して非同期的にプロパティにアクセスする場合 (スロットル/デバウンスを行う場合など)、アクセスしたプロパティは消去される可能性があります。イベントをプールに戻さないようにする場合は、persist()
メソッドを使用できます。
持続なし(デフォルトの動作:プールされたイベント)
onClick = e => {
alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
setTimeout(() => {
alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
}, 0);
};
2 番目 (非同期) は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
読むジュレンの答えpersist()
スロットル/デバウンス機能を使用する例。