使用 React、react-final-form 和 lodash debounce 函数,我想验证用户名是否尚未使用(该字段正在使用 react-final-form)。
我在让 debounce 函数从获取请求返回已解决的承诺时遇到问题。
我提供了以下 codesandbox 链接来演示我的问题:
请有人告诉我为什么我的代码不起作用。
验证的入口点来自
中 validate 属性中引用的 this.isNameUnique 调用import React from "react";
import { Field, Form } from "react-final-form";
import { debounce } from "lodash";
class DemoForm extends React.Component {
getIsNameUnique = name => {
console.log("getIsNameUnique", name);
// fake fetch request
return async name => {
const asyncResponse = await new Promise(resolve =>
setTimeout(resolve({ name: true }), 1000)
);
console.log("async api response", asyncResponse);
return asyncResponse;
};
};
debounceCreativeName = name => {
console.log("debounceCreativeName", name);
return new Promise(resolve => {
debounce(() => resolve(this.getIsNameUnique(name)), 2000);
});
};
isNameUnique = async name => {
const isNameAvailable = await this.debounceCreativeName(name);
console.log("isNameAvailable", isNameAvailable);
return isNameAvailable;
};
render() {
return (
<Form
onSubmit={() => null}
render={({ handleSubmit, reset, submitting, pristine, values }) => {
return (
<form onSubmit={handleSubmit}>
<Field name="name" validate={this.isNameUnique}>
{({ input, meta }) => {
return (
<input
style={{
display: "flex",
height: "40px",
fontSize: "24px"
}}
autoComplete="off"
{...input}
type="text"
placeholder="Enter the name"
/>
);
}}
</Field>
</form>
);
}}
/>
);
}
}
export default DemoForm;
这个沙箱解决了您的问题。
您不应该在每个渲染上创建新的去抖函数:
return new Promise(resolve => {
debounce(() => resolve(this.getIsNameUnique(name)), 2000);
});
相反,你应该用去抖动包装整个函数
isNameUnique
(请参阅我的沙箱)。通过在每次点击时创建一个新的去抖函数,它无法“记住”哪个函数被调用或者哪个函数将被再次调用。 这将防止去抖。
此外,通过异步
getIsNameUnique
,您可以仅使用await 来降低其复杂性。
到目前为止我知道lodasahe的debounce不允许你返回promise,但你可以手动处理它!
我们在这里可以做的只是将去抖函数包装到另一个函数中,该函数将具有与原始去抖函数相同的参数接口。这个包装器将返回 Promise,并在 lodashe 的 debounce 被触发时管理我们的 Promise 解析执行。
这是具有泛型类型的打字稿示例。
export type FnWithAnyArgs<T = any> = (...args: any[]) => T | Promise<T>;
export function debouncePromisify<T = void>(
fn: FnWithAnyArgs<T>,
timeout: number
): (...args: Parameters<typeof fn>) => Promise<T> {
let resolver: ((value: T | Promise<T>) => void) | null = null;
const fnDebounced = _debounce((...args: Parameters<typeof fn>) => {
const res = fn(...args);
resolver?.(res);
resolver = null;
}, timeout);
return (...args: Parameters<typeof fn>) => {
return new Promise((resolve) => {
resolver = resolve;
fnDebounced(...args);
});
};
}
---- 使用示例
async function isNameUnique() {
let isUnique = true;
...your logic with the backend validation etc.
return isUnique;
}
...
const isNameUniqueDebounced = debouncePromisify<boolean>(
this.isNameUnique,
300
);
...
async function setName() {
... do things here like setting value or the loading status
// VS code will know that isAvailable is boolean automatically
const isAvailable = await this.isNameUniqueDebounced();
...do things only after the debounce will be executed
}