Table des matières
Contexte
Formulaire HTML
Enregistrer l'état du formulaire
Validation
Problèmes
Formulaire React Rx
React et RxJS
Idée de conception
formState 数据结构
数据流
核心组件
Field 组件
Form 组件
组件之间的通信
接口的设计
Enhance
FieldArray
FormValues
FormSection
测试
Unit Test
Integration Test
Auto Fill Form Util
Debug
打印 Log
Maison interface Web js tutoriel Introduction détaillée à l'implémentation RxJS de Redux Form (exemple de code)

Introduction détaillée à l'implémentation RxJS de Redux Form (exemple de code)

Dec 31, 2018 am 09:55 AM
form react.js rxjs 前端

Le contenu de cet article est une introduction détaillée (exemple de code) sur l'implémentation de Redux Form avec RxJS. Il a une certaine valeur de référence. J'espère que ce sera le cas. utile pour vous.

Avant de lire cet article, vous devez maîtriser les connaissances :

  • React

  • RxJS (au moins il faut savoir quel sujet is)

Contexte

La forme peut être considérée comme l'un des plus gros problèmes du développement Web. Par rapport aux composants ordinaires, le formulaire présente les caractéristiques suivantes :

  1. Plus d'interactions utilisateur.
    Cela signifie qu'un grand nombre de composants personnalisés peuvent être nécessaires, tels que DataPicker, Upload, AutoComplete, etc.

  2. Changements de statut fréquents.
    Chaque fois que l'utilisateur saisit une valeur, cela peut entraîner des modifications de l'état de l'application, nécessitant la mise à jour des éléments du formulaire ou l'affichage de messages d'erreur.

  3. La vérification du formulaire consiste à vérifier la validité des données saisies par l'utilisateur.
    Il existe de nombreuses formes de validation de formulaire, telles que la validation lors de la saisie, la validation après avoir perdu le focus, ou la validation avant de soumettre le formulaire, etc.

  4. Communication réseau asynchrone.
    Lorsque la saisie utilisateur et la communication réseau asynchrone existent en même temps, il y a plus à considérer. Par exemple, AutoComplete doit obtenir de manière asynchrone les données correspondantes en fonction de la saisie de l'utilisateur. Si l'utilisateur lance une requête à chaque fois qu'il saisit, cela entraînera un grand gaspillage de ressources. Étant donné que chaque entrée est obtenue par 异步, les données obtenues par deux entrées utilisateur consécutives peuvent également avoir un problème de « dernier arrivé, premier arrivé ».

En raison des caractéristiques ci-dessus, le développement de la forme devient difficile. Dans les prochains chapitres, nous combinerons RxJS et Form pour nous aider à mieux résoudre ces problèmes.

Formulaire HTML

Avant d'implémenter notre propre composant Form, faisons d'abord référence au formulaire HTML natif.

Enregistrer l'état du formulaire

Pour un composant Form, il est nécessaire d'enregistrer les informations de tous les éléments du formulaire (tels que la valeur, la validité, etc.), et le formulaire HTML ne fait pas exception.
Alors, où le formulaire HTML enregistre-t-il l'état du formulaire ? Comment puis-je obtenir des informations sur les éléments de formulaire ?

Il existe principalement les méthodes suivantes :

  1. document.forms renverra tous les <form></form> nœuds du formulaire.

  2. HTMLFormElement.elements Renvoie tous les éléments du formulaire.

  3. event.target.elements peut également obtenir tous les éléments du formulaire.

document.forms[0].elements[0].value; // 获取第一个 form 中第一个表单元素的值

const form = document.querySelector("form");
form.elements[0].value; 

form.addEventListener('submit', function(event) {
  console.log(event.target.elements[0].value);
});
Copier après la connexion

Validation

Les types de validation de formulaire sont généralement divisés en deux types :

  1. Formulaire intégré validation. Par défaut, il sera déclenché automatiquement lors de la soumission du formulaire. Vous pouvez désactiver la validation automatique du navigateur en définissant l'attribut novalidate.

  2. Validation JavaScript.

Copier après la connexion
           

Problèmes

  • La personnalisation est difficile. Par exemple, la validation en ligne n'est pas prise en charge, le formulaire ne peut être vérifié que lors de la soumission et le style du message d'erreur ne peut pas être personnalisé.

  • Il est difficile de traiter des scènes complexes. Par exemple, imbrication d'éléments de formulaire, etc.

  • Le composant Input se comporte de manière incohérente, ce qui rend difficile l'obtention de la valeur d'un élément de formulaire. Par exemple, la case à cocher et la sélection multiple ne peuvent pas obtenir directement la valeur lors de l'obtention de la valeur, et une conversion supplémentaire est requise.

var $form = document.querySelector('form');

function getFormValues(form) {
  var values = {};
  var elements = form.elements; // elemtns is an array-like object

  for (var i = 0; i <h2 id="Formulaire-React-Rx">Formulaire React Rx</h2><p>Les étudiants intéressés peuvent d'abord jeter un œil au code source https://github.com/reeli/reac...</p><h3 id="React-et-RxJS">React et RxJS</h3><p>RxJS est un outil de gestion de données très puissant, mais il n'a pas de fonction de rendu d'interface utilisateur, alors que React est particulièrement doué pour gérer les interfaces. Alors pourquoi ne pas combiner leurs atouts ? Résoudre notre puzzle de formulaire en utilisant React et RxJS. Maintenant que l'on connaît leurs atouts respectifs, la division du travail est relativement claire : </p><p><strong>RxJS est responsable de la gestion de l'état, et React est responsable du rendu de l'interface. </strong></p><h3 id="Idée-de-conception">Idée de conception</h3><p>Différent de Redux Form, nous ne stockerons pas l'état du formulaire dans le magasin, mais le sauvegarderons directement dans le composant <code><form></form></code>. Utilisez ensuite RxJS pour notifier chaque <code><field></field></code> des données, puis le composant <code><field></field></code> décidera s'il doit mettre à jour l'interface utilisateur en fonction des données. S'il doit être mis à jour, il appellera <code>setState</code>, sinon il le fera. ne fera rien. </p><p>Par exemple, supposons qu'il y ait trois champs dans un formulaire (comme indiqué ci-dessous lorsque seule la valeur de FieldA change, afin d'empêcher les sous-composants de </p>
Copier après la connexion
<form></form> de également). re-Render, Redux Form doit être restreint en interne via
. shouldComponentUpdate()
// 伪代码
Copier après la connexion
              
RxJS peut contrôler la granularité des mises à jour des composants au minimum. En d'autres termes,

restituer ce qui a vraiment besoin d'être restitué, mais les composants qui n'ont pas besoin d'être restitués. rendus ne sont pas restitués. <field></field>

Le noyau est le sujet

À partir des idées de conception ci-dessus, les deux problèmes suivants peuvent être résumés :

  1. Le formulaire et le champ ont un lien unique -plusieurs relations. Le statut du formulaire doit être notifié dans plusieurs champs.

  2. Le champ doit modifier l'état du composant en fonction des données.

第一个问题,需要的是一个 Observable 的功能,而且是能够支持多播的 Observable。第二个问题需要的是一个 Observer 的功能。在 RxJS 中,既是 Observable 又是 Observer,而且还能实现多播的,不就是 Subject 么!因此,在实现 Form 时,会大量用到 Subject。

formState 数据结构

Form 组件中也需要一个 State,用来保存所有 Field 的状态,这个 State 就是 formState。

那么 formState 的结构应该如何定义呢?

在最早的版本中,formState  的结构是长下面这个样子的:

interface IFormState {
  [fieldName: string]: {
    dirty?: boolean;
    touched?: boolean;
    visited?: boolean;
    error?: TError;
    value: string;
  };
}
Copier après la connexion

formState 是一个对象,它以 fieldName  为 key,以一个 保存了 Field 状态的对象作为它的 value。

看起来没毛病对吧?

但是。。。。。

最后 formState 的结构却变成了下面这样:

interface IFormState {
  fields: {
    [fieldName: string]: {
      dirty?: boolean;
      touched?: boolean;
      visited?: boolean;
      error?: string | undefined;
    };
  };
  values: {
    [fieldName: string]: any;
  };
}
Copier après la connexion

Note: fields 中不包含 filed value,只有 field 的一些状态信息。values 中只有 field values。

为什么呢???

其实在实现最基本的 Form 和 Field 组件时,以上两种数据结构都可行。

那问题到底出在哪儿?

这里先买个关子,目前你只需要知道 formState 的数据结构长什么样就可以了。

数据流

Introduction détaillée à limplémentation RxJS de Redux Form (exemple de code)

为了更好的理解数据流,让我们来看一个简单的例子。我们有一个 Form 组件,它的内部包含了一个 Field 组件,在 Field 组件内部又包含了一个 Text Input。数据流可能是像下面这样的:

  1. 用户在输入框中输入一个字符。

  2. Input 的 onChange  事件会被 Trigger。

  3. Field 的 onChange Action 会被 Dispatch。

  4. 根据 Field 的 onChange Action 对 formState 进行修改。

  5. Form State 更新之后会通知 Field 的观察者。

  6. Field 的观察者将当前 Field 的 State pick 出来,如果发现有更新则 setState ,如果没有更新则什么都不做。

  7. setState 会使 Field rerender ,新的 Field Value 就可以通知给 Input 了。

核心组件

首先,我们需要创建两个基本组件,一个 Field 组件,一个 Form 组件。

Field 组件

Field 组件是连接 Form 组件和表单元素的中间层。它的作用是让 Input 组件的职责更单一。有了它之后,Input 只需要做显示就可以了,不需要再关心其他复杂逻辑(validate/normalize等)。况且,对于 Input 组件来说,不仅可以用在 Form 组件中,也可以用在 Form 组件之外的地方(有些地方可能并不需要 validate 等逻辑),所以 Field 这一层的抽象还是非常重要的。

  • 拦截和转换。 format/parse/normalize。

  • 表单校验。 参考 HTML Form 的表单校验,我们可以把 validation 放在 Field 组件上,通过组合验证规则来适应不同的需求。

  • 触发 field 状态的 改变(如 touched,visited)

  • 给子组件提供所需信息。 向下提供 Field 的状态 (error, touched, visited...),以及用于表单元素绑定事件的回调函数 (onChange,onBlur...)。

利用 RxJS 的特性来控制 Field 组件的更新,减少不必要的 rerender。

与 Form 进行通信。 当 Field 状态发生变化时,需要通知 Form。在 Form 中改变了某个 Field 的状态,也需要通知给 Field。

Form 组件

  • 管理表单状态。 Form 组件将表单状态提供给 Field,当 Field 发生变化时通知 Form。

  • 提供 formValues。

  • 在表单校验失败的时候,阻止表单的提交。

    通知 Field 每一次 Form State 的变化。 在 Form 中会创建一个 formSubject$,每一次 Form State 的变化都会向 formSubject$ 上发送一个数据,每一个 Field 都会注册成为 formSubject$ 的观察者。也就是说 Field 知道 Form State 的每一次变化,因此可以决定在适当的时候进行更新。
    当 FormAction 发生变化时,通知给 Field。 比如 startSubmit 的时候。


组件之间的通信

  1. Form 和 Field 通信。

    Context 主要用于跨级组件通信。在实际开发中,Form 和 Field 之间可能会跨级,因此我们需要用 Context 来保证 Form 和 Field 的通信。Form 通过 context 将其 instance 方法和 formState 提供给 Field。

  2. Field 和 Form 通信。

    Form 组件会向 Field 组件提供一个 d__ispatch__ 方法,用于 Field 和 Form 进行通信。所有 Field 的状态和值都由 Form 统一管理。如果期望更新某个 Field 的状态或值,必须 dispatch 相应的 action。

  3. 表单元素和 Field 通信

    表单元素和 Field 通信主要是通过回调函数。Field 会向表单元素提供 onChange,onBlur 等回调函数。

接口的设计

对于接口的设计来说,简单清晰是很重要的。所以 Field 只保留了必要的属性,没有将表单元素需要的其他属性通过 Field 透传下去,而是交给表单元素自己去定义。

通过 Child Render,将对应的状态和方法提供给子组件,结构和层级更加清晰了。

Field:

type TValidator = (value: string | boolean) => string | undefined;

interface IFieldProps {
  children: (props: IFieldInnerProps)=> React.ReactNode;
  name: string;
  defaultValue?: any;
  validate?: TValidator | TValidator[];
}
Copier après la connexion

Form:

interface IRxFormProps {
  children: (props: IRxFormInnerProps) => React.ReactNode;
  initialValues?: {
      [fieldName: string]: any;
  }
}
Copier après la connexion

到这里,一个最最基本的 Form 就完成了。接下来我们会在它的基础上进行一些扩展,以满足更多复杂的业务场景。

Enhance

FieldArray

FieldArray 主要用于渲染多组 Fields。

回到我们之前的那个问题,为什么要把 formState 的结构分为 fileds 和 values?

其实问题就出在 FieldArray,

  • 初始长度由 initLength 或者 formValues 决定。

  • formState 整体更新。

FormValues

通过 RxJS,我们将 Field 更新的粒度控制到了最小,也就是说如果一个 Field 的 Value 发生变化,不会导致 Form 组件和其他 Feild 组件 rerender。

既然 Field 只能感知自己的 value 变化,那么问题就来了,如何实现 Field 之间的联动?

于是 FormValues 组件就应运而生了。

每当 formValues 发生变化,FormValues 组件会就把新的 formValues 通知给子组件。也就是说如果你使用了 FormValues  组件,那么每一次 formValues 的变化都会导致 FormValues 组件以及它的子组件 rerender,因此不建议大范围使用,否则可能带来性能问题。

总之,在使用 FormValues 的时候,最好把它放到一个影响范围最小的地方。也就是说,当 formValues 发生变化时,让尽可能少的组件 rerender。

在下面的代码中,FieldB 的显示与否需要根据 FieldA 的 value 来判断,那么你只需要将 FormValues 作用于 FIeldA 和 FieldB 就可以了。

<formvalues>
    {({ formValues, updateFormValues }) => (
        
            <fielda></fielda>
            {!!formValues.A && <fieldb></fieldb>}
        >
    )}
</formvalues>
Copier après la connexion

FormSection

FormSection 主要是用于将一组 Fields group 起来,以便在复用在多个 form 中复用。主要是通过给 name添加前缀来实现的。

那么怎样给 Field 和 FieldArray 的 name 添加前缀呢?

我首先想到的是通过 React.Children 拿到子组件的 name,再和 FormSection 的 name 拼接起来。

但是,FormSection 和 Field 有可能不是父子关系!因为 Field 组件还可以被抽成一个独立的组件。因此,存在跨级组件通信的问题。

没错!跨级组件通信我们还是会用到 context。不过这里我们需要先从 FormConsumer 中拿到对应的 context value,再通过 Provider 将 prefix 提供给 Consumer。这时 Field/FieldArray 通过 Consumer 拿到的就是 FormSection 中的 Provider 提供的值,而不再是由 Form 组件的 Provider 所提供。因为 Consumer 会消费离自己最近的那个 Provider 提供的值。

<formconsumer>
  {(formContextValue) => {
    return (
      <formprovider>
        {children}
      </formprovider>
    );
  }}
</formconsumer>
Copier après la connexion

测试

Unit Test

主要用于工具类方法。

Integration Test

主要用于 Field,FieldArray 等组件。因为它们不能脱离 Form 独立存在,所以无法对其使用单元测试。

Note: 在测试中,无法直接修改 instance 上的某一个属性,以为 React 将 props 上面的节点都设置成了 readonly (通过 Object.defineProperty 方法)。 但是可以通过整体设置 props 绕过。

instance.props = {
  ...instance.props,
  subscribeFormAction: mockSubscribeFormAction,
  dispatch: mockDispatch,
};
Copier après la connexion

Auto Fill Form Util

如果项目中的表单过多,那么对于 QA 测试来说无疑是一个负担。这个时候我们希望能够有一个自动填表单的工具,来帮助我们提高测试的效率。

在写这个工具的时候,我们需要模拟 Input 事件。

input.value = 'v';
const event = new Event('input', {bubbles: true});
input.dispatchEvent(event);
Copier après la connexion

我们的期望是,通过上面的代码去模拟 DOM 的 input 事件,然后触发 React 的 onChange 事件。但是 React 的 onChange 事件却没有被触发。因此无法给 input 元素设置 value。

因为 ReactDOM 在模拟 onChange 事件的时候有一个逻辑:只有当 input 的 value 改变,ReactDOM 才会产生 onChange 事件。

React 16+ 会覆写 input value setter,具体可以参考 ReactDOM 的 inputValueTracking。因此我们只需要拿到原始的 value setter,call 调用就行了。

const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, "v");

const event = new Event("input", { bubbles: true});
input.dispatchEvent(event);
Copier après la connexion

Debug

打印 Log

在 Dev 环境中,可以通过 Log 来进行 Debug。目前在 Dev 环境下会自动打印 Log,其他环境则不会打印 Log。
Log 的信息主要包括: prevState, action, nextState。

Note:  由于 prevState, action, nextState 都是 Object,所以别忘了在打印的时候调用 cloneDeep,否则无法保证最后打印出来的值的正确性,也就是说最后得到的结果可能不是打印的那一时刻的值。


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Meilleurs paramètres graphiques
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌
Will R.E.P.O. Vous avez un jeu croisé?
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Un article pour parler du contrôle de la mémoire dans Node Un article pour parler du contrôle de la mémoire dans Node Apr 26, 2023 pm 05:37 PM

Le service Node construit sur une base non bloquante et piloté par les événements présente l'avantage d'une faible consommation de mémoire et est très adapté à la gestion de requêtes réseau massives. Dans le contexte de demandes massives, les questions liées au « contrôle de la mémoire » doivent être prises en compte. 1. Le mécanisme de récupération de place du V8 et les limitations de mémoire Js sont contrôlés par la machine de récupération de place

Découvrez comment écrire des tests unitaires dans Vue3 Découvrez comment écrire des tests unitaires dans Vue3 Apr 25, 2023 pm 07:41 PM

Vue.js est devenu aujourd'hui un framework très populaire dans le développement front-end. À mesure que Vue.js continue d'évoluer, les tests unitaires deviennent de plus en plus importants. Aujourd'hui, nous allons explorer comment écrire des tests unitaires dans Vue.js 3 et fournir quelques bonnes pratiques ainsi que des problèmes et solutions courants.

Parlons en profondeur du module File dans Node Parlons en profondeur du module File dans Node Apr 24, 2023 pm 05:49 PM

Le module de fichiers est une encapsulation des opérations de fichiers sous-jacentes, telles que l'ajout de lecture/écriture/ouverture/fermeture/suppression de fichiers, etc. La plus grande caractéristique du module de fichiers est que toutes les méthodes fournissent deux versions de **synchrone** et ** asynchrone**, with Les méthodes avec le suffixe sync sont toutes des méthodes de synchronisation, et celles qui n'en ont pas sont toutes des méthodes hétérogènes.

Comment résoudre les problèmes cross-domaines ? Une brève analyse des solutions courantes Comment résoudre les problèmes cross-domaines ? Une brève analyse des solutions courantes Apr 25, 2023 pm 07:57 PM

Le cross-domaine est un scénario souvent rencontré en développement, et c'est également une question souvent abordée lors des entretiens. La maîtrise des solutions interdomaines communes et des principes qui les sous-tendent peut non seulement améliorer notre efficacité de développement, mais également mieux performer lors des entretiens.

PHP et Vue : une combinaison parfaite d'outils de développement front-end PHP et Vue : une combinaison parfaite d'outils de développement front-end Mar 16, 2024 pm 12:09 PM

PHP et Vue : une combinaison parfaite d'outils de développement front-end À l'ère actuelle de développement rapide d'Internet, le développement front-end est devenu de plus en plus important. Alors que les utilisateurs ont des exigences de plus en plus élevées en matière d’expérience des sites Web et des applications, les développeurs front-end doivent utiliser des outils plus efficaces et plus flexibles pour créer des interfaces réactives et interactives. En tant que deux technologies importantes dans le domaine du développement front-end, PHP et Vue.js peuvent être considérés comme une arme parfaite lorsqu'ils sont associés. Cet article explorera la combinaison de PHP et Vue, ainsi que des exemples de code détaillés pour aider les lecteurs à mieux comprendre et appliquer ces deux éléments.

Questions fréquemment posées par les enquêteurs front-end Questions fréquemment posées par les enquêteurs front-end Mar 19, 2024 pm 02:24 PM

Lors des entretiens de développement front-end, les questions courantes couvrent un large éventail de sujets, notamment les bases HTML/CSS, les bases JavaScript, les frameworks et les bibliothèques, l'expérience du projet, les algorithmes et les structures de données, l'optimisation des performances, les requêtes inter-domaines, l'ingénierie front-end, les modèles de conception et les nouvelles technologies et tendances. Les questions de l'intervieweur sont conçues pour évaluer les compétences techniques du candidat, son expérience en matière de projet et sa compréhension des tendances du secteur. Par conséquent, les candidats doivent être parfaitement préparés dans ces domaines pour démontrer leurs capacités et leur expertise.

En savoir plus sur les tampons dans Node En savoir plus sur les tampons dans Node Apr 25, 2023 pm 07:49 PM

Au début, JS ne fonctionnait que du côté du navigateur. Il était facile de traiter les chaînes codées en Unicode, mais il était difficile de traiter les chaînes binaires et non codées en Unicode. Et le binaire est le format de données le plus bas du package ordinateur, vidéo/audio/programme/réseau.

Comment utiliser le langage Go pour le développement front-end ? Comment utiliser le langage Go pour le développement front-end ? Jun 10, 2023 pm 05:00 PM

Avec le développement de la technologie Internet, le développement front-end est devenu de plus en plus important. La popularité des appareils mobiles, en particulier, nécessite une technologie de développement frontal efficace, stable, sûre et facile à entretenir. En tant que langage de programmation en développement rapide, le langage Go est utilisé par de plus en plus de développeurs. Alors, est-il possible d’utiliser le langage Go pour le développement front-end ? Ensuite, cet article expliquera en détail comment utiliser le langage Go pour le développement front-end. Voyons d’abord pourquoi le langage Go est utilisé pour le développement front-end. Beaucoup de gens pensent que le langage Go est un

See all articles