本题参考了React-Admin框架,以及如何使用作者推荐的方法实现一个通用的特性。不是关于如何使用纯 React 从 API 请求数据。
以 https://marmelab.com/react-admin/Validation.html#async-validation 中的代码为例:
const validateUserCreation = async (values) => {
const errors = {};
// removed other checks that were here...
const isEmailUnique = await checkEmailIsUnique(values.email);
if (!isEmailUnique) {
// Return a message directly
errors.email = 'Email already used';
}
return errors
};
export const UserCreate = () => (
<Create>
<SimpleForm validate={validateUserCreation}>
<TextInput label="First Name" source="firstName" />
<TextInput label="Email" source="email" />
<TextInput label="Age" source="age" />
</SimpleForm>
</Create>
);
您将如何编写
checkEmailIsUnique
函数来从 UserCreate
组件查询 API,同时使用 dataProvider 实例挂钩,而不是按照作者的建议编写我们自己的 fetch 语句?
我有一个类似的场景,我需要在创建或更新记录之前验证字段值的唯一性。你能帮帮我吗?
====更新===
我写了一个这样的组件:
import { TextInput, useDataProvider } from "react-admin";
const ERRORTEXT = "Name is not unique";
interface PartnerNameInputProps {
source: string;
[key: string]: any;
}
/**
*
* `PartnerNameInput` is used to validate if the entered value is available.
* @example
* <PartnerNameInput source='name' />
*
* In case of an invalid name, you can customize the error message using
* `errortext` prop.
*/
const PartnerNameInput = ({
source,
...props
}: PartnerNameInputProps): JSX.Element => {
if (!source || !source.trim()) {
throw new Error(`'source' must be a non-empty string`);
}
const dataProvider = useDataProvider();
const { validate = [], errortext = ERRORTEXT, ...rest } = props;
async function validateNameUnique(name: string) {
if (!name) {
return undefined;
}
let isvalid = true;
try {
const { data } = await dataProvider.getList("partners", {
filter: { name },
pagination: { page: 1, perPage: 10 },
sort: { field: "id", order: "DESC" },
});
isvalid = data.length === 0;
} catch (e) {
console.error(e);
}
return isvalid ? undefined : errorobj;
}
const errorobj = { message: errortext };
validate.push(validateNameUnique);
return <TextInput source={source} validate={validate} {...rest} />;
};
export default PartnerNameInput;
但是这个不解决更新动作,只解决创建动作。仍在寻找更好的解决方案。
谢谢你,
我有一个类似的 issue 并且无法弄清楚如何使用我的数据提供者解决它,所以我在我的 react-admin 应用程序中使用了 axios。
因此,这并不能完全回答你的问题。
但是以防万一这种方法有帮助,对您来说等效的是这样的:
const checkEmailIsUnique = async (enteredEmail) => {
const data = await axios(withAuth({url:`${API_URL}/emails/${enteredEmail}`}))
return (data?.length > 0)
}
const validateUserCreation = async (values) => {
const errors = {}
if (values.email) {
if (await checkEmailIsUnique(values.email)) {
errors.email = ‘email already used’
}
} else {
errors.email = 'Email is required'
}
...
return errors
}
…
在我以前的版本的基础上,我编写了一个组件来验证给定资源/集合中值的唯一性。
我的场景做了一些假设,最明显的是:每条记录都有一个
id
字段,DataProvider是firebase。
这个适用于创建和更新!
import firebase from "firebase/compat/app";
import { TextInput, useDataProvider, useRecordContext } from "react-admin";
const ERRORTEXT = "Value is not unique";
export interface Filter {
collectionQuery: (
c: firebase.firestore.CollectionReference
) => firebase.firestore.Query;
}
export interface UniqueValueInputProps {
source: string;
resource: string;
validate?: any[] | any;
errortext?: string;
[key: string]: any;
}
interface BuildUniqueValueFilterParams {
fieldPath: string | firebase.firestore.FieldPath;
value: any;
recordId?: any;
}
export function buildUniqueValueFilter({
fieldPath,
value,
recordId,
}: BuildUniqueValueFilterParams): Filter {
return {
collectionQuery: (collection) =>
collection
.where(fieldPath, "==", value)
.where("id", "!=", recordId ?? ""),
};
}
/**
*
* `UniqueValueInput` UniqueValueInput is a custom input component used in
* React Admin applications for validating if the entered value is unique.
* It performs validation by checking if the entered value exists in a specified
* resource, excluding the current record (if any) being edited.
* @example
* <UniqueValueInput source='name' resource='users' />
*
* In case of an invalid value, you can customize the error message using the
* `errortext` prop.
* @example
* <UniqueValueInput source='name' resource='users'
* errortext='This name is already taken' />
*
* An optional array of validation functions can be passed to the `validate` prop.
* If provided, these functions will be called after the uniqueness check.
* Each validation function should return undefined if the validation passes,
* or an object with a message property if it fails.
* @example
* <UniqueValueInput source='name' resource='users'
* validate={[validateLenght]} />
*/
const UniqueValueInput = ({
source,
resource,
...props
}: UniqueValueInputProps): JSX.Element => {
if (!source?.trim()) {
throw new Error("'source' must be a non-empty string");
}
if (!resource?.trim()) {
throw new Error("'resource' must be a non-empty string");
}
const { validate = [], errortext = ERRORTEXT, ...rest } = props;
const errorobj = { message: errortext };
const record = useRecordContext();
const recordId = record?.id;
const dataProvider = useDataProvider();
async function validateUnique(value: string) {
if (!value) {
console.log("skip remote validation for empty value");
return undefined;
}
const { data } = await dataProvider.getList(resource, {
filter: buildUniqueValueFilter({ fieldPath: source, value, recordId }),
pagination: { page: 1, perPage: 1 },
sort: { field: "id", order: "DESC" },
});
return data.length === 0 ? undefined : errorobj;
}
validate.push(validateUnique);
return <TextInput source={source} validate={validate} {...rest} />;
};
export default UniqueValueInput;
用作:
export const UserCreate = (props: any) => (
<Create {...props} validate={validate}>
<SimpleForm>
<UniqueValueInput
source="name"
resource="users"
validate={[required()]}
/>
</SimpleForm>
</Create>
);