Lodash 使用 React Input 去抖动
Lodash debounce with React Input
我正在尝试将使用 lodash 的去抖动添加到搜索函数中,该函数是从输入 onChange 事件调用的。下面的代码生成类型错误 'function is expected',我理解这是因为 lodash 需要一个函数。执行此操作的正确方法是什么?可以全部在线完成吗?到目前为止,我已经尝试了几乎所有示例,但都无济于事。
search(e){
let str = e.target.value;
debounce(this.props.relay.setVariables({ query: str }), 500);
},
这不是一个简单的问题
一方面,为了解决您遇到的错误,您需要将 setVariables
包含在函数中:
search(e){
let str = e.target.value;
_.debounce(() => this.props.relay.setVariables({ query: str }), 500);
}
另一方面,我认为去抖动逻辑必须封装在 Relay 中。
debounce 函数可以在 JSX 中内联传递或直接设置为 class 方法,如下所示:
search: _.debounce(function(e) {
console.log('Debounced Event:', e);
}, 1000)
Fiddle: https://jsfiddle.net/woodenconsulting/69z2wepo/36453/
如果您使用的是 es2015+,则可以在 constructor
或 componentWillMount
.
等生命周期方法中直接定义 debounce 方法
示例:
class DebounceSamples extends React.Component {
constructor(props) {
super(props);
// Method defined in constructor, alternatively could be in another lifecycle method
// like componentWillMount
this.search = _.debounce(e => {
console.log('Debounced Event:', e);
}, 1000);
}
// Define the method directly in your class
search = _.debounce((e) => {
console.log('Debounced Event:', e);
}, 1000)
}
这就是我在谷歌搜索一整天后不得不这样做的方式。
const MyComponent = (props) => {
const [reload, setReload] = useState(false);
useEffect(() => {
if(reload) { /* Call API here */ }
}, [reload]);
const callApi = () => { setReload(true) }; // You might be able to call API directly here, I haven't tried
const [debouncedCallApi] = useState(() => _.debounce(callApi, 1000));
function handleChange() {
debouncedCallApi();
}
return (<>
<input onChange={handleChange} />
</>);
}
@Aximili
const [debouncedCallApi] = useState(() => _.debounce(callApi, 1000));
看起来很奇怪 :) 我预先使用 useCallback
:
的解决方案
const [searchFor, setSearchFor] = useState('');
const changeSearchFor = debounce(setSearchFor, 1000);
const handleChange = useCallback(changeSearchFor, []);
对于你的情况,应该是:
search = _.debounce((e){
let str = e.target.value;
this.props.relay.setVariables({ query: str });
}, 500),
class MyComp extends Component {
debounceSave;
constructor(props) {
super(props);
}
this.debounceSave = debounce(this.save.bind(this), 2000, { leading: false, trailing: true });
}
save()是要调用的函数
debounceSave() 是您实际调用(多次)的函数。
这对我有用:
handleChange(event) {
event.persist();
const handleChangeDebounce = _.debounce((e) => {
if (e.target.value) {
// do something
}
}, 1000);
handleChangeDebounce(event);
}
我发现这里的很多答案都过于复杂或不准确(即实际上没有去抖动)。这是一个简单的检查解决方案:
const [count, setCount] = useState(0); // simple check debounce is working
const handleChangeWithDebounce = _.debounce(async (e) => {
if (e.target.value && e.target.value.length > 4) {
// TODO: make API call here
setCount(count + 1);
console.log('the current count:', count)
}
}, 1000);
<input onChange={handleChangeWithDebounce}></input>
const delayedHandleChange = debounce(eventData => someApiFunction(eventData), 500);
const handleChange = (e) => {
let eventData = { id: e.id, target: e.target };
delayedHandleChange(eventData);
}
使用功能性 React 组件尝试使用 useCallback
。 useCallback
记住你的去抖功能,这样当组件重新渲染时它就不会一次又一次地重新创建。如果没有 useCallback
,去抖功能将不会与下一次击键同步。
`
import {useCallback} from 'react';
import _debounce from 'lodash/debounce';
import axios from 'axios';
function Input() {
const [value, setValue] = useState('');
const debounceFn = useCallback(_debounce(handleDebounceFn, 1000), []);
function handleDebounceFn(inputValue) {
axios.post('/endpoint', {
value: inputValue,
}).then((res) => {
console.log(res.data);
});
}
function handleChange (event) {
setValue(event.target.value);
debounceFn(event.target.value);
};
return <input value={value} onChange={handleChange} />
}
`
这是正确的FC方法
@
Aximili 回答只会触发一次
import { SyntheticEvent } from "react"
export type WithOnChange<T = string> = {
onChange: (value: T) => void
}
export type WithValue<T = string> = {
value: T
}
// WithValue & WithOnChange
export type VandC<A = string> = WithValue<A> & WithOnChange<A>
export const inputValue = (e: SyntheticEvent<HTMLElement & { value: string }>): string => (e.target as HTMLElement & { value: string }).value
const MyComponent: FC<VandC<string>> = ({ onChange, value }) => {
const [reload, setReload] = useState(false)
const [state, setstate] = useState(value)
useEffect(() => {
if (reload) {
console.log('called api ')
onChange(state)
setReload(false)
}
}, [reload])
const callApi = () => {
setReload(true)
} // You might be able to call API directly here, I haven't tried
const [debouncedCallApi] = useState(() => _.debounce(callApi, 1000))
function handleChange(x:string) {
setstate(x)
debouncedCallApi()
}
return (<>
<input
value={state} onChange={_.flow(inputValue, handleChange)} />
</>)
}
有些答案忽略了如果您想使用事件对象 (e) 中的 e.target.value 之类的东西,当您通过去抖动函数传递它时,原始事件值将为 null。
查看此错误消息:
警告:出于性能原因,此合成事件被重复使用。如果您看到此信息,则表示您正在 released/nullified 合成事件中访问 属性 nativeEvent
。这被设置为空。如果您必须保留原始合成事件,请使用 event.persist()。
如消息所述,您必须在事件函数中包含 e.persist()。例如:
const onScroll={(e) => {
debounceFn(e);
e.persist();
}}
当然,为了使用 React.useCallback(),您的 debounceFn 需要在 return 语句之外,这是必要的。我的 debounceFn 看起来像这样:
const debounceFn = React.useCallback(
_.debounce((e) =>
calculatePagination(e),
500, {
trailing: true,
}
),
[]
);
改进这个答案:
已知使用 useCallback
和 debounce
会导致 eslint exhaustive deps
警告。
以下是如何使用功能组件和useMemo
import { useMemo } from 'react';
import { debounce } from 'lodash';
import axios from 'axios';
function Input() {
const [value, setValue] = useState('');
const debounceFn = useMemo(() => debounce(handleDebounceFn, 1000), []);
function handleDebounceFn(inputValue) {
axios.post('/endpoint', {
value: inputValue,
}).then((res) => {
console.log(res.data);
});
}
function handleChange (event) {
setValue(event.target.value);
debounceFn(event.target.value);
};
return <input value={value} onChange={handleChange} />
}
我们正在使用 useMemo
到 return 一个记忆值,其中该值是由 debounce
编辑的函数 return
我正在尝试将使用 lodash 的去抖动添加到搜索函数中,该函数是从输入 onChange 事件调用的。下面的代码生成类型错误 'function is expected',我理解这是因为 lodash 需要一个函数。执行此操作的正确方法是什么?可以全部在线完成吗?到目前为止,我已经尝试了几乎所有示例,但都无济于事。
search(e){
let str = e.target.value;
debounce(this.props.relay.setVariables({ query: str }), 500);
},
这不是一个简单的问题
一方面,为了解决您遇到的错误,您需要将 setVariables
包含在函数中:
search(e){
let str = e.target.value;
_.debounce(() => this.props.relay.setVariables({ query: str }), 500);
}
另一方面,我认为去抖动逻辑必须封装在 Relay 中。
debounce 函数可以在 JSX 中内联传递或直接设置为 class 方法,如下所示:
search: _.debounce(function(e) {
console.log('Debounced Event:', e);
}, 1000)
Fiddle: https://jsfiddle.net/woodenconsulting/69z2wepo/36453/
如果您使用的是 es2015+,则可以在 constructor
或 componentWillMount
.
示例:
class DebounceSamples extends React.Component {
constructor(props) {
super(props);
// Method defined in constructor, alternatively could be in another lifecycle method
// like componentWillMount
this.search = _.debounce(e => {
console.log('Debounced Event:', e);
}, 1000);
}
// Define the method directly in your class
search = _.debounce((e) => {
console.log('Debounced Event:', e);
}, 1000)
}
这就是我在谷歌搜索一整天后不得不这样做的方式。
const MyComponent = (props) => {
const [reload, setReload] = useState(false);
useEffect(() => {
if(reload) { /* Call API here */ }
}, [reload]);
const callApi = () => { setReload(true) }; // You might be able to call API directly here, I haven't tried
const [debouncedCallApi] = useState(() => _.debounce(callApi, 1000));
function handleChange() {
debouncedCallApi();
}
return (<>
<input onChange={handleChange} />
</>);
}
@Aximili
const [debouncedCallApi] = useState(() => _.debounce(callApi, 1000));
看起来很奇怪 :) 我预先使用 useCallback
:
const [searchFor, setSearchFor] = useState('');
const changeSearchFor = debounce(setSearchFor, 1000);
const handleChange = useCallback(changeSearchFor, []);
对于你的情况,应该是:
search = _.debounce((e){
let str = e.target.value;
this.props.relay.setVariables({ query: str });
}, 500),
class MyComp extends Component {
debounceSave;
constructor(props) {
super(props);
}
this.debounceSave = debounce(this.save.bind(this), 2000, { leading: false, trailing: true });
}
save()是要调用的函数
debounceSave() 是您实际调用(多次)的函数。
这对我有用:
handleChange(event) {
event.persist();
const handleChangeDebounce = _.debounce((e) => {
if (e.target.value) {
// do something
}
}, 1000);
handleChangeDebounce(event);
}
我发现这里的很多答案都过于复杂或不准确(即实际上没有去抖动)。这是一个简单的检查解决方案:
const [count, setCount] = useState(0); // simple check debounce is working
const handleChangeWithDebounce = _.debounce(async (e) => {
if (e.target.value && e.target.value.length > 4) {
// TODO: make API call here
setCount(count + 1);
console.log('the current count:', count)
}
}, 1000);
<input onChange={handleChangeWithDebounce}></input>
const delayedHandleChange = debounce(eventData => someApiFunction(eventData), 500);
const handleChange = (e) => {
let eventData = { id: e.id, target: e.target };
delayedHandleChange(eventData);
}
使用功能性 React 组件尝试使用 useCallback
。 useCallback
记住你的去抖功能,这样当组件重新渲染时它就不会一次又一次地重新创建。如果没有 useCallback
,去抖功能将不会与下一次击键同步。
`
import {useCallback} from 'react';
import _debounce from 'lodash/debounce';
import axios from 'axios';
function Input() {
const [value, setValue] = useState('');
const debounceFn = useCallback(_debounce(handleDebounceFn, 1000), []);
function handleDebounceFn(inputValue) {
axios.post('/endpoint', {
value: inputValue,
}).then((res) => {
console.log(res.data);
});
}
function handleChange (event) {
setValue(event.target.value);
debounceFn(event.target.value);
};
return <input value={value} onChange={handleChange} />
}
`
这是正确的FC方法 @
Aximili 回答只会触发一次
import { SyntheticEvent } from "react"
export type WithOnChange<T = string> = {
onChange: (value: T) => void
}
export type WithValue<T = string> = {
value: T
}
// WithValue & WithOnChange
export type VandC<A = string> = WithValue<A> & WithOnChange<A>
export const inputValue = (e: SyntheticEvent<HTMLElement & { value: string }>): string => (e.target as HTMLElement & { value: string }).value
const MyComponent: FC<VandC<string>> = ({ onChange, value }) => {
const [reload, setReload] = useState(false)
const [state, setstate] = useState(value)
useEffect(() => {
if (reload) {
console.log('called api ')
onChange(state)
setReload(false)
}
}, [reload])
const callApi = () => {
setReload(true)
} // You might be able to call API directly here, I haven't tried
const [debouncedCallApi] = useState(() => _.debounce(callApi, 1000))
function handleChange(x:string) {
setstate(x)
debouncedCallApi()
}
return (<>
<input
value={state} onChange={_.flow(inputValue, handleChange)} />
</>)
}
有些答案忽略了如果您想使用事件对象 (e) 中的 e.target.value 之类的东西,当您通过去抖动函数传递它时,原始事件值将为 null。
查看此错误消息:
警告:出于性能原因,此合成事件被重复使用。如果您看到此信息,则表示您正在 released/nullified 合成事件中访问 属性 nativeEvent
。这被设置为空。如果您必须保留原始合成事件,请使用 event.persist()。
如消息所述,您必须在事件函数中包含 e.persist()。例如:
const onScroll={(e) => {
debounceFn(e);
e.persist();
}}
当然,为了使用 React.useCallback(),您的 debounceFn 需要在 return 语句之外,这是必要的。我的 debounceFn 看起来像这样:
const debounceFn = React.useCallback(
_.debounce((e) =>
calculatePagination(e),
500, {
trailing: true,
}
),
[]
);
改进这个答案:
已知使用 useCallback
和 debounce
会导致 eslint exhaustive deps
警告。
以下是如何使用功能组件和useMemo
import { useMemo } from 'react';
import { debounce } from 'lodash';
import axios from 'axios';
function Input() {
const [value, setValue] = useState('');
const debounceFn = useMemo(() => debounce(handleDebounceFn, 1000), []);
function handleDebounceFn(inputValue) {
axios.post('/endpoint', {
value: inputValue,
}).then((res) => {
console.log(res.data);
});
}
function handleChange (event) {
setValue(event.target.value);
debounceFn(event.target.value);
};
return <input value={value} onChange={handleChange} />
}
我们正在使用 useMemo
到 return 一个记忆值,其中该值是由 debounce