如何在 React-Admin 中使用 DataProvider 进行服务器端异步验证?

问题描述 投票:0回答:2

本题参考了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;

但是这个不解决更新动作,只解决创建动作。仍在寻找更好的解决方案。

谢谢你,

reactjs react-admin
2个回答
1
投票

我有一个类似的 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
}

…

0
投票

在我以前的版本的基础上,我编写了一个组件来验证给定资源/集合中值的唯一性。

我的场景做了一些假设,最明显的是:每条记录都有一个

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>
);
© www.soinside.com 2019 - 2024. All rights reserved.