Förbättra React forms med Redux Form

Intro

Redux är ett utmärkt verktyg för att dramatiskt förbättra datahanteringen i din applikation. Då borde det vara ett naturligt steg att använda det också för dina application forms. Absolut, men varför återuppfinna hjulet igen?

Redux Form gör exakt det du kan förvänta dig – det använder application state för att hantera form data utan att du behöver skriva all kod själv.

I det här inlägget kommer jag gå igenom processen med att konfigurera Redux-form och ge några exempel på typiska fall.

Setup

Redux Form-inställningen innehåller följande steg:

Inkludera form reducer i combineReducers function (det måste göras en gång per form i din applikation)

import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
const rootReducer = combineReducers({  
/* Other reducers */  
    form: formReducer
});
export default rootReducer;

Link selected form to form reducer (låt oss säga att du har en komponent som heter LoginPage)

import {reduxForm} from "redux-form";

export default reduxForm({ 
  form: 'login' /* This must be unique */ 
})(LoginPage);

I själva komponenten måste du länka din form till en funktion som kommer att utföras när du submittar. Detta görs via “handleSubmit” som tillhandahålls av Redux Form.

const {handleSubmit} = props
return (
    <form onSubmit={handleSubmit(login)}>
      <Field
        name='username'
        placeholder = "Enter username"
        component="input"type='text'
      />
      <Field
        name='password'
        placeholder = "Enter password"
        component="input"
        type='password'
      />
      <button type="submit">Login</button>
    </form>
)

Out form innehåller två inmatningsfält (för användarnamn och lösenord) och en knapp.

Inloggningsfunktionen kan vara en komponentfunktion eller en redux-åtgärd – vi behöver bara lägga till den med hjälp av anslutningsfunktionen. Det kommer att användas för asynkron validering.

import {login} from "../actions/actions";

/* If you need to connect your component to redux,
it's best to do it before applying reduxForm HOC*/
LoginPage = connect (mapStateToProps, {login})(LoginPage);

export default reduxForm({
    form: 'login'    
})(LoginPage);

Synkron validering

För att minska belastningen på back-end och förbättra användarupplevelsen är det bäst att utföra synkron validering när det är möjligt – till exempel kan vi kontrollera om användarnamnet och/eller lösenordet innehåller ogiltiga tecken.

Det första steget är att lägga till valideringsfunktionen via reduxForm HOC (higher-order component):

export default reduxForm({
    form: 'login',
    validate
})(LoginPage);

Och lägg till själva valideringsfunktionen ovanför LoginPage container.

const validate = (values) => {
    const errors = {}
    if(!values.username) {
        errors.username = "Cannot be empty"
    }
    if(!values.password) {
        errors.password = "Cannot be empty"
    }
    return errors
}

När vi ändrar användarnamn eller lösenord, kommer den synkrona valideringen att köras och kontrollera om båda fälten är populated. Om en valideringsfunktion returnerar errors så kommer formuläret inte att skickas in.

Asynkron validering

Om form values framgångsrikt överför synkroniserad validering så skickas de vanligtvis till en server via ett asynkront anrop. Med hjälp av Redux-formuläret kan vi inaktivera formuläret tills vår begäran har behandlats och ett felmeddelande visas om det inte lyckades.

handleSubmit kommer utföra vår inloggningsåtgärd:

export function login(values) {    
    return (dispatch) => {        
        return ApiConnector.login(values.username, values.password)
            .then(response => {
                if(response.data.success === true){
                  dispatch({
                    type: LOGIN
                  })
                } else {
                  throw new SubmissionError({        
                    _error: "Login failed!"
                    })

                }
            }) 
            .catch((error) => {              
              throw new SubmissionError({        
                _error: "Login failed!"
              })
            })
    }
}

Eftersom denna åtgärd använder ett asynkront anrop, måste vi använda Redux Thunk middleware. Funktionen ApiConnector.login är en post request som skickas med hjälp av axios. För att asynkron validering ska fungera korrekt är det viktigt att returnera Promise-objektet.

Vi kan se att om förfrågan till servern misslyckas (till exempel om en server är nere) eller inloggningen misslyckas, då returneras ett felmeddelande tillbaka till vårt formulär.

Hur kan vi visa felmeddelandet? Vi ändrar vår form container för att extrahera felet från props och visa den ovanför knappen “Logga in”.

const {handleSubmit, error} = props
return (
  <form onSubmit={handleSubmit(login)}>
    <Field
      name='username'
      placeholder = "Enter username"
      component="input"
      type='text'
    />
    <Field
      name='password'
      placeholder = "Enter password"
      component="input"
      type='password'
    />
    {error && <strong>{error}</strong>}
    <button type="submit">Login</button>
  </form>
)

Initial values

Många gånger vill du att dina forms ska ha några initiala värden (möjligen baserat på tidigare användarval). Detta kan göras genom att lägga till initialValues-objektet till container props (till exempel genom mapStateToProps).

function mapStateToProps(state) {
  return {
    initialValues: {
      username: "admin"
    }
  }
}

Vanligtvis kommer initialvärden att fyllas på från application state.

Changing values programmatically

Det finns situationer, när ett värde ändras, eller du automatiskt vill ändra ett annat värde – till exempel om någon vill logga in med “gäst” användarnamn, så ändras lösenordet automatiskt till “gäst” också.

Först måste funktionen “ChangeChange” läggas till med reduxForm HOC.

export default reduxForm({
    form: 'login',
    validate,
    onChange
})(LoginPage);

onChange-funktionen kan läggas till ovanför Login Page komponenten.

import {reduxForm, change} from "redux-form";

const onChange = (values, dispatch, props, previousValues) => {
  if(values.username === "guest") {
    dispatch( change ('login', 'password', 'guest');
  }
}

Detta kan vara väl användbart om du har ett värde som är helt beroende av ett annat.

Sammanfattning

Genom att använda informationen i det här inlägget så bör du kunna implementera Redux Form i dina applikationer. Det är ett mycket kraftfullt verktyg, som i stort sett innehåller all den funktionalitet som behövs för hantering av application forms.

Om du behöver mer information så är det bästa stället att hitta det på här: www.redux-form.com

/ Radek

Origin: www.linkedin.com

Alla artiklar