Hallo!
Wir haben ein Admin-Panel und viele Formulare im BCS, aber in der React-Community gibt es keine allgemein akzeptierte Methode - wie man sie für die Wiederverwendung entwirft. Der offizielle Facebook-Leitfaden enthält keine detaillierten Informationen zum Arbeiten mit Formularen unter realen Bedingungen, bei denen eine Validierung und Wiederverwendung erforderlich ist. Jemand verwendet Redux-Form, Formik, Final-Form oder schreibt sogar seine eigene Lösung.
In diesem Artikel zeigen wir eine der Optionen für die Arbeit mit Formularen in React. Unser Stack wird folgendermaßen aussehen: React + formik + Typescript. Wir werden zeigen:
- Was eine Komponente tun sollte.
- Konfiguration, Felder und Validierung auf Requisitenebene.
- So machen Sie ein Formular wiederverwendbar.
- Optimierung des Renderers.
- Dann ist unsere Methode unpraktisch.
Mit der neuen Geschäftsaufgabe haben wir gelernt, dass wir 15 bis 20 ähnliche Formen erstellen müssen, und hypothetisch gibt es möglicherweise noch mehr davon. Wir hatten ein Dinosaurier-Formular in der Konfiguration, das mit Daten aus dem "Store" arbeitete und Aktionen zum Speichern und Ausführen von Anforderungen über "Sagas" sendete. Sie war wunderbar und machte geschäftlichen Wert. Aber es war bereits nicht erweiterbar und nicht wiederverwendbar, nur mit schlechtem Code und dem Hinzufügen von Krücken.
Die Aufgabe besteht darin, das Formular so umzuschreiben, dass es unbegrenzt oft wiederverwendet werden kann. Denken Sie an die funktionale Programmierung, sie hat reine Funktionen, die keine externen Daten verwenden, in unserem Fall "Redux", sondern nur das, was sie in Argumenten (Requisiten) gesendet werden.
Und genau das ist passiert.
Die Idee unserer Komponente ist, dass Sie einen Wrapper (Container) erstellen und darin die Logik der Arbeit mit der Außenwelt schreiben (Empfangen von Daten aus dem Redux-Speicher und Senden von Aktionen). Dazu muss die Containerkomponente über die Rückrufe einige Informationen empfangen können. Die ganze Liste der Form Requisiten:
interface IFormProps {
Verwenden von Formik
Wir verwenden die <Formik /> -Komponente.
render() { const { fields, validationSchema, validateOnBlur = true, validateOnChange = true, } = this.props; return ( <Formik initialValues={fields} render={this.renderForm} onSubmit={this.handleSubmitForm} validationSchema={validationSchema} validateOnBlur={validateOnBlur} validateOnChange={validateOnChange} validate={this.validateFormLevel} /> ); }
Im Prop'e des "validate" -Formulars rufen wir die "this.validateFormLevel" -Methode auf, bei der wir der Containerkomponente die Möglichkeit geben, alle geänderten Felder abzurufen und zu überprüfen, ob sie gültig sind.
private validateFormLevel = (values: FormFields) => { const { onChangeFields, validationSchema } = this.props; if (onChangeFields) { validationSchema .validate(values) .then(() => { onChangeFields(values, { isValid: true }); }) .catch(() => { onChangeFields(values, { isValid: false }); }); } }
Hier müssen wir die Validierung erneut aufrufen, um dem Container klar zu machen, ob die Felder gültig sind. Beim Absenden eines Formulars rufen wir einfach prop `onSubmit` auf:
private handleSubmitForm = (): void => { const { onSubmit } = this.props; if (onSubmit) { onSubmit(); } }
Mit den Requisiten 1-5 sollte alles klar sein. Fahren wir mit 'config', 'fields' und 'validationSchema' fort.
Requisiten 'config'
interface IFieldsFormMetaModel { sectionName?: string; sectionDescription?: string; fieldsForm?: Array<{ name?: string;
Basierend auf dieser Schnittstelle erstellen wir ein Array von Objekten und rendern "Abschnitt" -> "Abschnittsfelder" nach diesem Schema. So können wir mehrere Felder für den Abschnitt oder in jedem gleichzeitig anzeigen, wenn Sie einen Titel und eine Notiz benötigen. Wie das Rendern funktioniert, zeigen wir etwas später.
Ein kurzes Beispiel für eine Konfiguration:
export const config: IFieldsFormMetaModel[] = [ { sectionName: ' ', fieldsForm: [{ name: 'subject', label: '', type: ElementTypes.Text, }], }, { sectionName: '', sectionDescription: ' ', fieldsForm: [{ name: 'reminder', disabled: true, label: '', type: ElementTypes.CheckBox, checked: true, }], }, ];
Basierend auf Geschäftsdaten werden die Werte für die Namensschlüssel festgelegt. Die gleichen Werte werden in den Tasten der Prop-Felder verwendet, um die ursprünglichen oder geänderten Werte für den Formic zu übertragen.
Im obigen Beispiel könnten "Felder" folgendermaßen aussehen:
const fields: SomeBusinessApiFields = { subject: ' ', reminder: 'yes', }
Zur Validierung müssen wir das Yup-Schema übergeben. Wir geben das Formular an das Formular mit den Container-Requisiten weiter und beschreiben dort Interaktionen mit externen Daten, z. B. Anfragen.
Das Formular kann das Schema in keiner Weise beeinflussen, zum Beispiel:
export const CreateClientSchema: ( props: CreateClientProps, ) => Yup.MixedSchema = (props: CreateClientProps) => Yup.object( { subject: Yup.string(), description: Yup.string(), date: dateSchema, address: addressSchema(props), }, );
Render- und Feldoptimierung
Zum Rendern haben wir eine Karte für die schnelle Suche nach Schlüsseln erstellt. Es sieht prägnant aus und die Suche ist schneller als mit "switch".
fieldsMap: Record< ElementTypes, ( state: FormikFieldState, handlers: FormikHandlersState, field: IFieldsFormInfo, ) => JSX.Element > = { [ElementTypes.Text]: ( state: FormikFieldState, handlers: FormikHandlersState, field: IFieldsFormInfo ) => { const { values, errors, touched } = state; return ( <FormTextField key={field.name} element={field} handleChange={this.handleChangeField(handlers.setFieldValue, field.name)} handleBlur={handlers.handleBlur} value={values[field.name]} error={touched[field.name] && errors[field.name] || ''} /> ); }, [ElementTypes.TextSearch]: (...) => {...}, [ElementTypes.TextArea]: (...) => {...}, [ElementTypes.Date]: (...) => {...}, [ElementTypes.CheckBox]: (...) => {...}, [ElementTypes.RadioButton]: (...) => {...}, [ElementTypes.Select]: (...) => {...}, };
Jedes Komponentenfeld ist statusbehaftet. Es befindet sich in einer separaten Datei und ist in "React.memo" eingeschlossen. Alle Werte werden über Requisiten übertragen, wobei die "Kinder" umgangen werden, um unnötigen Renderer zu vermeiden.
Fazit
Unser Formular ist nicht ideal, für jeden Fall müssen wir einen Container-Wrapper für die Arbeit mit Daten erstellen. Speichern Sie sie im "Store", konvertieren Sie und stellen Sie Anfragen. Es gibt eine Wiederholung von Code, den Sie entfernen möchten. Wir versuchen eine neue Lösung zu finden, bei der die Form abhängig von den Requisiten den gewünschten Schlüssel mit Feldern, Aktionen, Diagrammen und Konfigurationen aus dem Geschäft entnimmt. In einem der folgenden Beiträge werden wir Ihnen erzählen, was daraus geworden ist.