Handling and validating user input in a React app

Edward Byne

Software Engineer

GitHub/Twitter: ejbyne

Introduction

  • Forms are everywhere on the web
  • They look easy, but handling input and validation can be complex:
    • Where do you store input data?
    • How do you handle server errors?
    • Do you need frontend validation?
    • How do you provide the user with a seamless experience?
  • There's no one way to get it right

Storing data

  • Where do you store the user input?
  • 3 main options:
    • Redux store
    • Local component state
    • Find an npm library which manages the data for you e.g. redux-form
  • My preference is local state
this.setState({ localState: 😍 })

My approach to state

  • Each application is different, so it's not one size fits all
  • But as a rule of thumb:
    • Redux store for server data and global state
    • Local state for isolated data
  • Why do people hate setState?

It's not just me

Using local component state is fine. As a developer, it is your job to determine what kinds of state make up your application, and where each piece of state should live. Find a balance that works for you, and go with it.

Use React for ephemeral state that doesn't matter to the app globally and doesn't mutate in complex ways. For example, a toggle in some UI element, a form input state. Use Redux for state that matters globally or is mutated in complex ways. For example, cached users, or a post draft.

Sometimes you'll want to move from Redux state to React state (when storing something in Redux gets awkward) or the other way around (when more components need to have access to some state that used to be local).

The rule of thumb is: do whatever is less awkward.

Basic form

Example

Frontend validation

The wrong way

  • Instinctive approach of many developers is to start by handling validation directly in the UI component
  • Either in the component itself or an imported utility function
  • But this is business logic - we should keep this logic separate from our UI components

Frontend validation

A better approach

  • Let the UI components focus purely on UI logic
  • Let the Redux layer handle any business logic
  • Pass in validation callbacks as props, which are called by event handlers
  • Pass in validation errors as props, which are then simply displayed

But...

  • It brings additional complexity
  • Our backend will have its own validation layer
  • Why not start there first and test the UX experience?
  • We need to be able to parse backend errors anyway and can always add frontend validation later
  • Some constraints can't be validated in the frontend e.g. unique username

Do we actually need frontend validation?

Backend validation

Example

Parsing server errors

Backend

Async action creator

UI Form

Post request

Error

Action creator

Error

Submit callback

Reducer

Error action

Store

Updated state (with error)

Selector

Parsed error

Raw error

User friendly messages

Example

 

Maybe that's enough?

Or maybe the backend can offer pre-submission validation endpoints? 

e.g. /api/reviews?validate=true

...Or maybe not?

  • May not be suitable for large forms, where user needs regular feedback
  • May not be suitable for mobile apps

So, if we do need frontend validation...

 

  1. Replicate the backend validation rules in frontend code
  2. Or, preferably, fetch the validation rules from the backend and use them to generate JavaScript validators

2 options:

Backend API schemas

  • Schema - metadata which tells us how request and response payloads are structured
  • JSON Schema is the most well-known standard specification for defining JSON payloads
  • We can write a JS function to read this schema and generate a validator
  • Existing JS validator libraries for JSON Schema
  • e.g. Enjoi, AJV

Swagger

and the Open API Specification

  • Specification for defining RESTful APIs
  • YAML or JSON format
  • Uses a subset of JSON Schema to define request and response payloads
  • Lots of Swagger libraries available for generating code based on OpenAPI document and vice versa
  • e.g. SpringFox, hapi-swagger

Frontend validation

Example

Further considerations

  • Keep the form logic generic and reusable
  • Internationalisation techniques
  • Use the backend schema data to set constraints on input fields and prevent validation errors

Conclusion

  • Don't be afraid to use component state
  • Frontend validation is not easy
  • Start with backend validation
  • Error messages format should be defined in API documentation at an early stage
  • If you need frontend validation, consider ways of sharing validation logic between frontend and backend
  • Keep business logic separate from UI logic

Edward Byne

Software Engineer

Thank you

Handling and validating user input in a React app

By Edward Byne

Handling and validating user input in a React app

  • 632