Lodash debounce TypeError: Mong đợi một hàm phản ứng
Tôi đang cố gắng sử dụng debouncechức năng này để tránh nhiều cuộc gọi khi tôi đang nhập và tìm kiếm nội dung nào đó trong một cơ sở dữ liệu. Những gì tôi đang làm bây giờ là đầu vào
onChange={(e) => {
const delayedQuery = useCallback(debounce(this.handleSearch(e.target.value), 500));
return delayedQuery
}}
nơi handeSearchlà
handleSearch(filter) {
service.getData(filter).subscribe((data) => {console.log(data)})
}
nhưng tôi có lỗi này TypeError: Expected a function. Dịch vụ đang hoạt động nhưng không có lỗi. Nó viết từng ký tự cùng lúc tôi đang gõ và điều đó không chính xác.
Trả lời
Bạn không thể sử dụng hook trong các thành phần lớp hoặc trong các hàm xử lý sự kiện và bạn phải chuyển một hàm để khắc phục lỗi.
Cũng thế; nếu bạn cố gắng đọc target.valuekhông đồng bộ từ một sự kiện, bạn sẽ nhận được sự kiện tổng hợp này được sử dụng lại vì lỗi lý do hiệu suất .
Trong trò chuyện, tôi giả sử bạn sử dụng rxjs from(promise)vì vậy bạn vẫn gặp sự cố khi người dùng nhập và bạn kích hoạt yêu cầu không đồng bộ, họ có thể giải quyết theo một thứ tự khác.
Xem bên dưới để biết một ví dụ bị hỏng (gõ nhanh và chờ 2 giây, nó sẽ tự giải quyết ha)
//simple debounce implementation
const debounce = (fn, delay = 50) => {
let time;
return (...args) => {
clearTimeout(time);
time = setTimeout(() => fn(...args), delay);
};
};
//service where you can subscribe to
const service = (() => {
let listeners = [];
const later = (value, time = 50) =>
new Promise((r) =>
//if search is 2 characters long it'll resolve much later
setTimeout(
() => r(value),
value.length === 2 ? 2000 : time
)
);
//subscribe returns an unsubscribe function
const subScribe = (fn) => {
listeners.push(fn);
//return the Subscriber object
//https://rxjs-dev.firebaseapp.com/api/index/class/Subscriber
return {
unsubscribe: () =>
(listeners = listeners.filter(
(listener) => listener !== fn
)),
};
};
//notify all listeners
const notify = (value) =>
listeners.forEach((listener) => listener(value));
return {
getData: (value) => ({
subScribe: (fn) => {
const unsub = subScribe(fn);
later(value).then((value) => notify(value));
return unsub;
},
}),
};
})();
class App extends React.PureComponent {
constructor(props) {
super(props);
this.state = { asyncSearchResult: '' };
//use handleSearch to create a debounced version of it
this.handleSearchDebounced = debounce(
this.handleSearch
).bind(this); //you need to bind it to guearantee correct context
}
rendered = 0;
handleSearch = (value) =>
service
.getData(value)
.subScribe((value) =>
this.setState({ asyncSearchResult: value })
);
render() {
return (
<div>
<input
type="text"
// pass value to the handler, not the event
onChange={(e) =>
this.handleSearchDebounced(e.target.value)
}
/>
async result: {this.state.asyncSearchResult}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Đây là giải pháp:
//simple debounce implementation
const debounce = (fn, delay = 50) => {
let time;
return (...args) => {
clearTimeout(time);
time = setTimeout(() => fn(...args), delay);
};
};
//service where you can subscribe to
const service = (() => {
let listeners = [];
const later = (value, time = 50) =>
new Promise((r) =>
//if search is 2 characters long it'll resolve much later
setTimeout(
() => r(value),
value.length === 2 ? 2000 : time
)
);
//subscribe returns an unsubscribe function
const subScribe = (fn) => {
listeners.push(fn);
//return the Subscriber object
//https://rxjs-dev.firebaseapp.com/api/index/class/Subscriber
return {
unsubscribe: () =>
(listeners = listeners.filter(
(listener) => listener !== fn
)),
};
};
//notify all listeners
const notify = (value) =>
listeners.forEach((listener) => listener(value));
return {
getData: (value) => ({
subScribe: (fn) => {
const unsub = subScribe(fn);
later(value).then((value) => notify(value));
return unsub;
},
}),
};
})();
const latestGetData = (value) => {
const shared = {};
return {
subScribe: (fn) => {
const current = {};
shared.current = current;
const subscriber = service
.getData(value)
.subScribe((result) => {
//only call subscriber of latest resolved
if (shared.current === current) {
fn(result);
}
subscriber.unsubscribe();
});
},
};
};
class App extends React.PureComponent {
constructor(props) {
super(props);
this.state = { asyncSearchResult: '' };
//use handleSearch to create a debounced version of it
this.handleSearchDebounced = debounce(
this.handleSearch
).bind(this); //you need to bind it to guearantee correct context
}
rendered = 0;
handleSearch = (value) =>
latestGetData(value).subScribe((value) =>
this.setState({ asyncSearchResult: value })
);
render() {
return (
<div>
<input
type="text"
// pass value to the handler, not the event
onChange={(e) =>
this.handleSearchDebounced(e.target.value)
}
/>
async result: {this.state.asyncSearchResult}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>