Lodash debounce TypeError: Expected a function react
Lodash debounce TypeError: Expected a function react
我正在尝试使用 debounce
函数来避免在数据表中键入和搜索内容时进行多次调用。我现在做的是在input
onChange={(e) => {
const delayedQuery = useCallback(debounce(this.handleSearch(e.target.value), 500));
return delayedQuery
}}
其中 handeSearch
是
handleSearch(filter) {
service.getData(filter).subscribe((data) => {console.log(data)})
}
但我有这个错误 TypeError: Expected a function
。该服务正在运行,但没有去抖动。它在我打字的同时一个字符一个字符地写入,这是不正确的。
你不能在 class 组件或事件处理函数中使用钩子,你必须将函数传递给 lodash debounce。
还有;如果您尝试从事件中异步读取 target.value
,您将收到 this synthetic event is reused for performance reasons 错误。
在聊天中我假设你使用 rxjs from(promise)
所以你仍然有一个问题,当用户输入并且你触发异步请求时,他们可能会以不同的顺序解析。
查看下面的错误示例(快速输入 hai 并等待 2 秒,它解析为 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>
解决方法如下:
//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>
我正在尝试使用 debounce
函数来避免在数据表中键入和搜索内容时进行多次调用。我现在做的是在input
onChange={(e) => {
const delayedQuery = useCallback(debounce(this.handleSearch(e.target.value), 500));
return delayedQuery
}}
其中 handeSearch
是
handleSearch(filter) {
service.getData(filter).subscribe((data) => {console.log(data)})
}
但我有这个错误 TypeError: Expected a function
。该服务正在运行,但没有去抖动。它在我打字的同时一个字符一个字符地写入,这是不正确的。
你不能在 class 组件或事件处理函数中使用钩子,你必须将函数传递给 lodash debounce。
还有;如果您尝试从事件中异步读取 target.value
,您将收到 this synthetic event is reused for performance reasons 错误。
在聊天中我假设你使用 rxjs from(promise)
所以你仍然有一个问题,当用户输入并且你触发异步请求时,他们可能会以不同的顺序解析。
查看下面的错误示例(快速输入 hai 并等待 2 秒,它解析为 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>
解决方法如下:
//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>