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?
- When do you validate?
- How do you display error messages?
- There's no one way to get it right, but the backend API schema and frontend architecture should be considered at an early stage
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
- Redux store for server data and global state
- Local state for isolated data
- If only the component needs to know about the data, why share it with the whole application?
- U.I. state e.g. is the dropdown open
- Form data - is only temporary, will be thrown away
- Each application is different, so it's not one size fits all
- I personally found redux-form too restrictive and it introduces a Redux dependency in your UI components
- Why do people hate setState?

It's not just me
- Redux docs say it's OK!
- Redux creator Dan Abramov recommends it!
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
- Local state
- material-ui form components
- It's easy if the user gets it right!
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
- Examples:
- Name cannot be longer than 20 characters
- Date must be in the future
- 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/development time
- Our backend will have its own validation layer, which will send back validation error messages
- 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
- Node.js server
- Hapi framework
- Built-in validation using Hapi's joi library
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
- When validate query present then validate without processing further
Or maybe not?
- May not be suitable for large forms e.g. wizards with multiple steps, where user needs regular feedback
- May not be suitable for mobile apps, where user is offline or has slow network connection
So, if we do need frontend validation...
2 options:
- Replicate the backend validation rules in frontend code
- Or, preferably, fetch the validation rules from the backend and use them to generate JavaScript validators
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
- If we agree on a schema spec with our backend team, we can write a JS function to read this schema and generate a validator
- There are existing JS validator libraries for JSON Schema
- But how can we generate these schemas?
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
- 2 approaches:
- Design driven - produce code based on OpenAPI document
- Use libraries to automatically generate OpenAPI document based on code
- Lots of Swagger libraries available for generating code based on OpenAPI document and vice versa
- e.g. SpringFox, hapi-swagger
Frontend validation
Example
- I used Enjoi to make it easy for me (creates a Joi validator out of a JSON Schema)
- Other popular JSON Schema validator libraries e.g. AJV
- Validator called on input blur and prior to submission
- Error messages passed back to UI component via the same "errors" prop
- Validation error only shown for field if it has been clicked/tabbed into
Further considerations
- Keep the form logic generic - it can then be made reusable using the HOC or render prop patterns
- Internationalisation techniques to provide user friendly messages
- Use the backend schema data to set constraints on input fields and prevent validation errors e.g.
- extract enum values into a dropdown list
- set maxlength on text input
Conclusion
- Don't be afraid to use component state
- Frontend validation is not easy
- Don't assume it's always necessary
- 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

GitHub/Twitter: ejbyne
Thank you
Copy of Handling and validating user input in a React app
By Edward Byne
Copy of Handling and validating user input in a React app
- 226