创建您自己的 React 验证库:开发者体验(第 3 部分)

已发表: 2022-03-10
快速总结 ↬所以我们已经看到了如何实现验证库的基本部分,以及如何添加我们需要的所有不错的功能。 本系列的最后一部分将专注于改善将使用我们的验证库的人的用户体验:开发人员。

如果您一直在关注这个小文章系列,那么您现在已经学会了如何组合您自己的验证库。 它几乎可以处理您可以提出的任何挑战,甚至可以帮助解决可访问性问题! 它唯一的缺点是使用起来很糟糕。

是的,没错。 从开发人员的角度来看,用户体验严重不足。 当我们拼错单词、滥用 API 或其他任何事情时,我们不会收到任何有用的警告!

本文将指导您了解如何改善您的验证库的开发人员体验——或任何为此目的的库。

  • 第 1 部分:基础知识
  • 第 2 部分:功能
  • 第 3 部分:体验

开始

从本文的最后一部分开始,我们已将所有库代码提取到其自己的文件中。 看看 CodeSandbox 演示,看看我们从什么开始。

跳跃后更多! 继续往下看↓

便利功能

我们希望我们的库尽可能简单地用于最常见的情况。 实现该目标的一种方法是为某些功能添加方便的实用程序功能。

一个这样的功能可能是检查我们的表单是否有效——也就是说,如果所有错误消息都是null 。 这是您通常在onSubmit处理程序中检查的内容,但它在您的渲染方法中也可能很有用。 让我们实现它!

 const isFormValid = useMemo( () => Object.values(errors).every(error => error === null), [errors] );

我们将在我们的onSubmit表单处理程序以及我们的渲染方法中提供这个标志。

  • 请参阅 CodeSandbox 演示

还有很多这些可以写,但我会让它成为读者的练习。

开发警告和不变量

React 最大的特性之一是它在开发过程中提供了许多有用的控制台警告。 我们也应该为我们的用户提供同样的质量。

首先,我们将创建两个函数——用于将warning记录到控制台的warning,以及用于抛出错误的invariant如果不满足给定条件的话。

 function warning(condition, message) { if (process.env.NODE_ENV === 'production' || condition) { return; } console.warn('useValidation: ' + message); } function invariant(condition, message) { if (process.env.NODE_ENV === 'production' || condition) { return; } throw new Error('useValidation: ' + message); }

如果错误会使您的库崩溃(或使其无用),您希望使用invariant ,并warning不良做法或其他建议。

何时发出警告

决定何时发出警告非常重要。 太多了,你只是烦人。 太少了,你会让关键的错误交付到生产环境中。 因此,我们需要明智地处理警告。

由于我们的库接受一个相当大的配置对象,因此以某种方式验证它是有意义的——至少在开发时是这样。 我们可以使用 TypeScript 或 Flow 之类的类型系统来解决它,但这会排除所有常规的 JavaScript 用户。

相反,让我们创建一个运行时模式检查器,我们在其中验证配置是否包含正确的字段,并打印相关警告。

 function validateConfigSchema(config) { if (process.env.NODE_ENV === 'production') { return; } if (typeof config === 'function') { config = config({}); } invariant( typeof config === 'object', `useValidation should be called with an object or a function returning an object. You passed a ${typeof config}.`, ); invariant( typeof config.fields === 'object', 'useValidation requires a `field` prop with an object containing the fields and their validators. Please refer to the documentation on usage: https://link.to/docs' ); invariant( Object.values(config.fields).every(field => typeof field === 'object'), 'useValidation requires that the `field` object only contains objects. It looks like yours isn\'t. Please refer to the documentation on usage: https://link.to/docs' ); warning( ['always', 'blur', 'submit', undefined].includes(config.showError), 'useValidation received an unsupported value in the `showError` prop. Valid values are "always", "blur" or "submit".' ) // And so on }

如果我们想花时间,我们可能会继续这样做一段时间。 你应该! 这是改善应用程序开发人员体验的好方法。

但是,您不必手动编写这些内容。 流行的对象模式验证库joi有一个浏览器端口,可以帮助创建一个非常好的运行时验证检查。 此外,如前所述,类型系统将有助于在编译时为使用该类型系统的用户捕获配置错误。

允许灵活性

良好的开发人员体验在很大程度上不会妨碍开发人员。 让我们看看可以改善这种体验的几种方法。

编写冲突的道具

首先,我们的 prop getter 将一些 props 应用于我们的输入和表单,这些 props 可能会被我们的消费者意外覆盖。 相反,让我们为我们的 prop getter 添加一个 prop override 对象,它将所有冲突的 prop 组合在一起。

下面是我们如何在getFieldProps中实现它:

 getFieldProps: (fieldName, overrides = {}) => ({ onChange: e => { const { value } = e.target; if (!config.fields[fieldName]) { return; } dispatch({ type: 'change', payload: { [fieldName]: value }, }); if (overrides.onChange) { overrides.onChange(e); } }, onBlur: e => { dispatch({ type: 'blur', payload: fieldName }); if (overrides.onBlur) { overrides.onBlur(e) } }, name: overrides.name || fieldName, value: state.values[fieldName] || '', }),

getFormProps中可以采用类似的方法。

帮助避免螺旋桨钻孔

一些表格可能很大并且分成几个部分。 我们应该提供一个上下文,而不是让我们的消费者钻到树上。 这样,他们可以访问我们从下面树中任何地方的自定义钩子返回的所有内容。

首先,让我们使用 React 的createContext方法创建一个 ValidationContext:

 export const ValidationContext = React.createContext({});

接下来,让我们创建一个组件ValidationProvider ,它在上下文中提供来自useValidation钩子的所有值:

 export const ValidationProvider = props => { const context = useValidation(props.config); return ( {props.children} ); }; export const ValidationProvider = props => { const context = useValidation(props.config); return ( {props.children} ); }; export const ValidationProvider = props => { const context = useValidation(props.config); return ( {props.children} ); };

现在,我们不再直接调用useValidation ,而是将表单包装在ValidationProvider组件中,并通过使用useContext挂钩访问验证道具( getFormPropserrors等)。 你会这样使用它:

 Import React, { useContext } from 'react'; import { ValidationContext } from './useValidation'; function UsernameForm(props) { const { getFieldProps, errors } = useContext(ValidationContext); return ( <> <input {...getFieldProps('username')} /> {errors.username && {errors.username}></span>} </> ); }

这样,您将获得两全其美! 对于那些简单的场景,您可以获得一个简单的钩子,并且您可以获得那些复杂部分所需的灵活性。

文档是关键

每当我使用不是我自己编写的库时,我都喜欢出色的文档。 但是你应该关注什么,你应该在哪里记录?

第一步应该是编写一个简单易懂的 README,其中包含现成的最基本的使用示例。 Andrew Healey 写了一篇关于如何编写好的 README 的精彩文章,我强烈建议您阅读。

当您创建了一个很好的 README 来吸引人们前进时,一个文档网站可能是一个好主意。 在这里,您可以放置​​更深入的 API 文档、典型用例的配方和一个很好的常见问题解答。

有很多很棒的工具可以用来生成文档网站。 我最喜欢的是来自 Facebook 的docusaurus (谦虚的吹嘘:我们在创建create-react-app网站时使用了它),但是那里有几个不错的选择。

我们不会在本文中介绍如何编写好的文档。 那里有几篇很好的文章——甚至是一个名为“Write the Docs”的社区。 他们写了一个很好的指南,告诉你如何开始编写出色的文档。

概括

通过本系列文章,我们创建了一个相当不错的验证库。 它有一个非常简单的 API、在您需要时的灵活性、良好的开发人员体验以及许多非常酷的特性。

我们已经逐步了解了我们是如何实现的,我希望您对如何制作自己的库以及如何制作人们喜欢使用的库有更深入的了解。

请在评论中让我知道您的想法,以及是否有某些部分您被卡住或难以理解。 随着反馈的不断涌现,我会尽力更新文章。

结束这篇文章——这是最终版本:

  • 请参阅 CodeSandbox 演示

谢谢阅读!