React Hook Form – Form Validation Hook based on Uncontrolled Components

Now this is an interesting approach, building further upon Uncontrolled Components:

The React Hook Form (react-hook-form) library provides a new form validation mechanism which embraces uncontrolled form validation and support controlled components.

The core idea is to register HTML input’s ref into the custom hook and therefore subscribe to its input value, validate and evaluate the form submission.

I guess this one makes more sense to many as it immediately gives you a values object containing all form values. Validation is also built-in

import React from "react";
import useForm from "react-hook-form";

const Example = () => {
  const { handleSubmit, register, errors } = useForm();
  const onSubmit = values => {
    console.log(values);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        name="email"
        ref={register({
          required: 'Required',
          pattern: {
            value: /^[A-Z0-9._%+-][email protected][A-Z0-9.-]+\.[A-Z]{2,4}$/i,
            message: "invalid email address"
          }
        })}
      />
      {errors.email && errors.email.message}

      <input
        name="username"
        ref={register({
          validate: value => value !== "admin" || "Nice try!"
        })}
      />
      {errors.username && errors.username.message}

      <button type="submit">Submit</button>
    </form>
  );
};

React Hook Form Presentation (Twitter Thread) →
React Hook Form →

💡 The React Hook Form Twitter Thread was compiled from its origin tweet to one single page using Threader

Symfony Form Validation: Validating a date range

One of the (Symfony based) PHP projects I’m working on contains a form which allows the user to generate video clips from CCTV footage. To do this the user can enter a start and stop DateTime. For this to work the submitted input data is then checked: both start and stop must be dates, and the stop date must be set to a point in time after the start date.

Symfony’s DateTime Constraint can make sure both entries are DateTime instances. To check whether that the end date is after the begin date, one can use the Callback Constraint. Injected into that callback is a ExecutionContextInterface by which you can access the form, and thus other form params.

Here’s an example with the inputs start and stop:

use Symfony\Component\Validator\Constraints;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

// …

$builder
    ->add('start', 'datetime', 
        'constraints' => [
            new Constraints\NotBlank(),
            new Constraints\DateTime(),
        ],
    ])
    ->add('stop', 'datetime', [
        'constraints' => [
            new Constraints\NotBlank(),
            new Constraints\DateTime(),
            new Constraints\Callback(function($object, ExecutionContextInterface $context) {
                $start = $context->getRoot()->getData()['start'];
                $stop = $object;

                if (is_a($start, \DateTime::class) && is_a($stop, \DateTime::class)) {
                    if ($stop->format('U') - $start->format('U') < 0) {
                        $context
                            ->buildViolation('Stop must be after start')
                            ->addViolation();
                    }
                }
            }),
        ],
    ]);

If you want to have a minimum duration between both start and stop, you can change the number 0 in the snippet above to any number of seconds.

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

React Pattern: Centralized PropTypes

Cory House:

In real apps with large objects, using PropTypes quickly leads to a lot of code. That’s a problem, because in React, you’ll often pass the same object to multiple components. Repeating these details in multiple component files breaks the DRY principle (don’t repeat yourself). Repeating yourself creates a maintenance problem. The solution? Centralize your PropTypes.

Using the shape function of the prop-types package it’s possible to define types yourself. You could have your own definitions an address or a user for example. These type definitions can then be used like other prop-types types.

Cory centralizes his (custom) PropTypes in types/index.js …

import { shape, number, string, oneOf } from 'prop-types';

export const addressType = shape({
    id: number.isRequired,
    street: string.isRequired,
    street2: string,
    city: string.isRequired,
    state: string.isRequired,
    postal: number.isRequired,
});

export const userType = shape({
  id: number,
  firstName: string.isRequired,
  lastName: string.isRequired,
  company: string,
  role: oneOf(['user', 'author']),
  address: addressType.isRequired,
});

… and then imports them when needed:

import React, { Component } from 'react';
import {userType} from './../types';

export default class User extends Component {
  static propTypes = {
    user: userType.isRequired,
  };

  // …
}

React Pattern: Centralized PropTypes →