reactjs - Hvordan bruker du react-hook-form med redux og typescript på riktig måte


Etter mange dager med å prøve å finne en guide som kunne vise meg alle disse tingene i ett eksempel, ga jeg til slutt opp og laget noe som fungerer gjennom ren makt, men jeg vet at dette er feil måte å gjøre det på. Jeg vet ikke nøyaktig hvorfor det er feil, men det kan ikke være så komplisert som jeg har gjort det til her. Dette er mine krav:

  1. bruk Redux for å opprettholde applikasjonstilstand
  2. bruk React Hook Form for skjemaer
  3. bruk Typescript
  4. skjemaer må ha validering på både klient- og serversiden

Her er hva jeg har:

import * as React from "react";
import { useForm } from "react-hook-form";
import { ErrorMessage } from '@hookform/error-message';
import { connect } from "react-redux";

import { MainMenu } from "./../components/MainMenu";

import { userSignupAction } from '../actions/authenticateActions';

interface FormInputs {
  singleErrorInput: string;
  userName: string;
  password: string;
  email: string;
  firstName: string;
  lastName: string;
}

export function Signup(props: any) {
  const { 
    register,  
    formState: { errors },
    setError,
    handleSubmit,
    clearErrors
  } = useForm<FormInputs>();

  const onSubmit = handleSubmit(async (data: FormInputs) => {
    props.dispatch(userSignupAction(data));
  });

  if (props.response.signup.userName) {
    if (!errors.userName) {
      console.log('set username error')
      setError('userName', {
        type: "server",
        message: props.response.signup.userName[0],
      });
    }
  }

  if (props.response.signup.email) {
    if (!errors.email) {
      setError('email', {
        type: "server",
        message: props.response.signup.email[0],
      });
    }
  }

  if (props.response.signup.password) {
    if (!errors.password) {
      setError('password', {
        type: "server",
        message: props.response.signup.password[0],
      });
    }
  }

  return (
    <div>
      <MainMenu role="anonymous"/>
      <div className="center70">
        <h2>Signup</h2>
        <form onSubmit={onSubmit}>
          <div>
            <label>Username</label>
            <input {...register("userName", { required: "Username is required"})}
              onClick={() => {
                console.log('clear the error');
                props.response.signup.userName = false
                clearErrors('userName');
              }} />
            { errors.userName && <ErrorMessage errors={errors} name="userName" />}
          </div>
          <div>
            <label>First Name</label>
            <input {...register("firstName")} />
          </div>
          <div>
            <label>Last Name</label>
            <input {...register("lastName")} />
          </div>
          <div>
            <label>Email</label>
            <input {...register("email", { required: "Email is required"})} />
            <ErrorMessage errors={errors} name="email" />
          </div>
          <div>
            <label>Password</label>
            <input {...register("password", { required: "Password is required"})} />
            <ErrorMessage errors={errors} name="password" />
          </div>
          <input type="submit" />
        </form>
      </div>
    </div>
  );
}

const mapStateToProps = (response: any) => ({
  response
});

export default connect(mapStateToProps)(Signup);

Merk, dette er ikke meg som prøver å få et svar for jobben min, dette er bare meg som prøver å lære denne teknologien riktig. Denne koden er all min kode og ikke en del av noen proprietær applikasjon (tydeligvis siden den ikke er god).

Vennligst hjelp

Mer info...

Min første utfordring her var å prøve å få en server-side valideringsfeil til å vises i front-end. Måten jeg prøvde å gjøre dette på i starten var ved å formatere responsen som et json-objekt akkurat slik feilen objektet skal se ut i react-hook-forms, som følger...

{
  "userName": ["User 'jimbob' already exists"],
  "email": ["User with email address '[email ' already exists"],
  "password": ["Password must be at least 6 characters"]
}

I begynnelsen forventet jeg at dette svarformatet magisk skulle fylle inn feilen, men det gjorde det ikke, så derfor måtte jeg legge til de tre klausulene for å sjekke props-verdien. Opprinnelig forårsaket dette en uendelig løkke, så jeg måtte legge til den andre sjekken for å se om errors.fieldName ikke var sann og bare bruke setError i de tilfellene (flott! tenkte jeg)

Men så var det neste problemet, hva skjer når du får en server-side valideringsfeil, men så går brukeren tilbake for å fikse det. Vel, etter min mening bør dette umiddelbart fjerne feilen i det øyeblikket brukeren klikker på feltet. Vel, dette skjedde ikke fordi selv om clearErrors-funksjonen gjorde jobben sin, så opphevet props-sjekken det umiddelbart, så jeg måtte legge til den ekstra linjen for å sette feltet i props til false. Hurra! Nå fungerer det, men til hvilken pris?

Denne koden fungerer teknisk sett nå, men fy søren, dette er en ordkrevende kode for et så enkelt skjema. Jeg kan ikke tro at dette er den riktige måten å gjøre ting på, det burde ikke ta over 100 linjer med kode å lage et enkelt skjema med 5 elementer.

Svar #1

Din eksisterende arbeidsflyt er for øyeblikket som følger. Send inn skjema -> sett respons -> gjengii -> les respons -> sett feil.

Et par ting å merke seg.

  1. Du bør ikke bruke et klient-side valideringsverktøy (ReactHookForm) for server-side validering/feil
  2. Ikke bruk setError innenfor komponentens render-metode
  3. Den asynkrone wrapperen rundt dispatch er overflødig
  4. Vurder kravene dine på nytt, fordi de ikke er fornuftige.

En bedre flyt ville enten være å ha din egen redux-mellomvare eller bruke redux thunk. Endre flyten til å være: Send inn -> Sjekk etter feil -> sett feil via tilbakeringing -> send respons -> sett og vis respons

Svar #2

Hvis du lager en React-app med hjelp av react-redux-biblioteket, finnes det innebygde hooks for,

  1. Hent dataene fra redux-tilstanden - useSelector()
  2. Og sende handlinger for å oppdatere redux-tilstanden - useDispatch()

Bruk av useSelector() :-

Så, du trenger ikke å bruke følgende kode for å koble redux state til props.

const mapStateToProps = (response:any) => ({
    response
});

Enkelt sagt, du bør importere useSelector() fra react-redux som følger.

import {useSelector} from 'react-redux';

Da kan du hente hvilken som helst data fra redux-tilstanden til komponenten din.

const user = useSelector(state => state.user);

Bruk av useDispatch() :-

Du kan bruke useDispatch() i stedet for å bruke props for å få dispatch(). Denne hooken returnerer en referanse til dispatch-funksjonen fra Redux-butikken. Du kan bruke den til å dispatchere handlinger etter behov.

import {useSelector, useDispatch} from 'react-redux';
const dispatch = useDispatch();

Du kan bruke dispatch-referanse hvor som helst du vil sende en handling, for eksempel i koden din i stedet for å bruke følgende kode,

const onSubmit = handleSubmit(async (data: FormInputs) => {
    props.dispatch(userSignupAction(data));
});

du kan bruke utsendelsesreferansen som vi opprettet som følger.

const onSubmit = handleSubmit(async (data: FormInputs) => {
    dispatch(userSignupAction(data));
});