• 18-19 College Green, Dublin 2
  • 01 685 9088
  • info@cunninghamwebsolutions.com
  • cunninghamwebsolutions
    Cunningham Web Solutions
    • Home
    • About Us
    • Our Services
      • Web Design
      • Digital Marketing
      • SEO Services
      • E-commerce Websites
      • Website Redevelopment
      • Social Media Services
    • Digital Marketing
      • Adwords
      • Social Media Services
      • Email Marketing
      • Display Advertising
      • Remarketing
    • Portfolio
    • FAQ’s
    • Blog
    • Contact Us
    MENU CLOSE back  

    Creating Your Own React Validation Library: The Features (Part 2)

    You are here:
    1. Home
    2. Web Design
    3. Creating Your Own React Validation Library: The Features (Part 2)
    Thumbnail for 23034
    Smashing Editorial

    Creating Your Own React Validation Library: The Features (Part 2)

    Creating Your Own React Validation Library: The Features (Part 2)

    Kristofer Selbekk

    2019-05-23T13:00:16+02:00
    2019-05-23T12:35:17+00:00

    Implementing a validation library isn’t all that hard. Neither is adding all of those extra features that make your validation library much better than the rest.

    This article will continue implementing the validation library we started implementing in the previous part of this article series. These are the features that are going to take us from a simple proof of concept to an actual usable library!

    • Part 1: The Basics
    • Part 2: The Features
    • Part 3: The Experience (Coming up next week)

    Only Show Validation On Submit

    Since we’re validating on all change events, we’re showing the user error messages way too early for a good user experience. There are a few ways we can mitigate this.

    The first solution is simply providing the submitted flag as a returned property of the useValidation hook. This way, we can check whether or not the form is submitted before showing an error message. The downside here is that our “show error code” gets a bit longer:

    <label>
      Username
      <br />
      <input {...getFieldProps('username')} />
      {submitted && errors.username && (
        <div className="error">{errors.username}</div>
      )}
    </label>
    

    Another approach is to provide a second set of errors (let’s call them submittedErrors), which is an empty object if submitted is false, and the errors object if it’s true. We can implement it like this:

    const useValidation = config => {
      // as before
      return {
        errors: state.errors,
        submittedErrors: state.submitted ? state.errors : {},
      };
    }
    

    This way, we can simply destructure out the type of errors that we want to show. We could, of course, do this at the call site as well — but by providing it here, we’re implementing it once instead of inside all consumers.

    • See CodeSandbox demo showing how submittedErrors can be used.

    Show Error Messages On-Blur

    A lot of people want to be shown an error once they leave a certain field. We can add support for this, by tracking which fields have been “blurred” (navigated away from), and returning an object blurredErrors, similar to the submittedErrors above.

    The implementation requires us to handle a new action type — blur, which will be updating a new state object called blurred:

    const initialState = {
      values: {},
      errors: {},
      blurred: {},
      submitted: false,
    };
    
    function validationReducer(state, action) {
      switch (action.type) {
        // as before
        case 'blur':
          const blurred = { 
            ...state.blurred, 
            [action.payload]: true 
          }; 
          return { ...state, blurred };
        default:
          throw new Error('Unknown action type');
      }
    }
    

    When we dispatch the blur action, we create a new property in the blurred state object with the field name as a key, indicating that that field has been blurred.

    The next step is adding an onBlur prop to our getFieldProps function, that dispatches this action when applicable:

    getFieldProps: fieldName => ({
      // as before
      onBlur: () => {
        dispatch({ type: 'blur', payload: fieldName });
      },
    }),
    

    Finally, we need to provide the blurredErrors from our useValidation hook so that we can show the errors only when needed.

    const blurredErrors = useMemo(() => {
        const returnValue = {};
        for (let fieldName in state.errors) {
          returnValue[fieldName] = state.blurred[fieldName]
            ? state.errors[fieldName]
            : null;
        }
        return returnValue;
      }, [state.errors, state.blurred]);
    return {
      // as before
      blurredErrors,
    };
    

    Here, we create a memoized function that figures out which errors to show based on whether or not the field has been blurred. We recalculate this set of errors whenever the errors or blurred objects change. You can read more about the useMemo hook in the documentation.

    • See CodeSandbox demo

    Time For A Tiny Refactor

    Our useValidation component is now returning three sets of errors — most of which will look the same at some point in time. Instead of going down this route, we’re going to let the user specify in the config when they want the errors in their form to show up.

    Our new option — showErrors — will accept either “submit” (the default), “always” or “blur”. We can add more options later, if we need to.

    function getErrors(state, config) {
      if (config.showErrors === 'always') {
        return state.errors;
      }
      if (config.showErrors === 'blur') {
        return Object.entries(state.blurred)
          .filter(([, blurred]) => blurred)
          .reduce((acc, [name]) => ({ ...acc, [name]: state.errors[name] }), {});
      }
      return state.submitted ? state.errors : {};
    }
    const useValidation = config => {
      // as before
      const errors = useMemo(
        () => getErrors(state, config), 
        [state, config]
      );
    
      return {
        errors,
        // as before
      };
    };
    

    Since the error handling code started to take most of our space, we’re refactoring it out into its own function. If you don’t follow the Object.entries and .reduce stuff — that’s fine — it’s a rewrite of the for...in code in the last section.

    If we required onBlur or instant validation, we could specify the showError prop in our useValidation configuration object.

    const config = {
      // as before
      showErrors: 'blur',
    };
    const { getFormProps, getFieldProps, errors } = useValidation(config);
    // errors would now only include the ones that have been blurred
    
    • See CodeSandbox demo

    Note On Assumptions

    “Note that I’m now assuming that each form will want to show errors the same way (always on submit, always on blur, etc). That might be true for most applications, but probably not for all. Being aware of your assumptions is a huge part of creating your API.”

    Allow For Cross-Validation

    A really powerful feature of a validation library is to allow for cross-validation — that is, to base one field’s validation on another field’s value.

    To allow this, we need to make our custom hook accept a function instead of an object. This function will be called with the current field values. Implementing it is actually only three lines of code!

    function useValidation(config) {
      const [state, dispatch] = useReducer(...);
      if (typeof config === 'function') {
        config = config(state.values);
      }
    }
    

    To use this feature, we can simply pass a function that returns the configuration object to useValidation:

    const { getFieldProps } = useValidation(fields => ({ 
      password: {
        isRequired: { message: 'Please fill out the password' },
      },
      repeatPassword: {
        isRequired: { message: 'Please fill out the password one more time' },
        isEqual: { value: fields.password, message: 'Your passwords don't match' }
      }
    }));
    

    Here, we use the value of fields.password to make sure two password fields contain the same input (which is terrible user experience, but that’s for another blog post).

    • See CodeSandbox demo that doesn’t let the username and the password be the same value.

    Add Some Accessibility Wins

    A neat thing to do when you’re in charge of the props of a field is to add the correct aria-tags by default. This will help screen readers with explaining your form.

    A very simple improvement is to add aria-invalid="true" if the field has an error. Let’s implement that:

    const useValidation = config => {
      // as before
      return {
        // as before
        getFieldProps: fieldName => ({
          // as before
          'aria-invalid': String(!!errors[fieldName]),
        }),
      }
    };
    

    That’s one added line of code, and a much better user experience for screen reader users.

    You might wonder about why we write String(!!state.errors[fieldName])? state.errors[fieldName] is a string, and the double negation operator gives us a boolean (and not just a truthy or falsy value). However, the aria-invalid property should be a string (it can also read “grammar” or “spelling”, in addition to “true” or “false”), so we need to coerce that boolean into its string equivalent.

    There are still a few more tweaks we could do to improve accessibility, but this seems like a fair start.

    Shorthand Validation Message Syntax

    Most of the validators in the calidators package (and most other validators, I assume) only require an error message. Wouldn’t it be nice if we could just pass that string instead of an object with a message property containing that string?

    Let’s implement that in our validateField function:

    function validateField(fieldValue = '', fieldConfig, allFieldValues) {
      for (let validatorName in fieldConfig) {
        let validatorConfig = fieldConfig[validatorName];
        if (typeof validatorConfig === 'string') {
          validatorConfig = { message: validatorConfig };
        }
        const configuredValidator = validators[validatorName](validatorConfig);
        const errorMessage = configuredValidator(fieldValue);
    
        if (errorMessage) {
          return errorMessage;
        }
      }
      return null;
    }
    

    This way, we can rewrite our validation config like so:

    const config = {
      username: {
        isRequired: 'The username is required',
        isEmail: 'The username should be a valid email address',
      },
    };
    

    Much cleaner!

    Initial Field Values

    Sometimes, we need to validate a form that’s already filled out. Our custom hook doesn’t support that yet — so let’s get to it!

    Initial field values will be specified in the config for each field, in the property initialValue. If it’s not specified, it defaults to an empty string.

    We’re going to create a function getInitialState, which will create the initial state of our reducer for us.

    function getInitialState(config) {
      if (typeof config === 'function') {
        config = config({});
      }
      const initialValues = {};
      const initialBlurred = {};
      for (let fieldName in config.fields) {
        initialValues[fieldName] = config.fields[fieldName].initialValue || '';
        initialBlurred[fieldName] = false;
      }
      const initialErrors = validateFields(initialValues, config.fields);
      return {
        values: initialValues,
        errors: initialErrors,
        blurred: initialBlurred,
        submitted: false,
      };
    }
    

    We go through all fields, check if they have an initialValue property, and set the initial value accordingly. Then we run those initial values through the validators and calculate the initial errors as well. We return the initial state object, which can then be passed to our useReducer hook.

    Since we’re introducing a non-validator prop into the fields config, we need to skip it when we validate our fields. To do that, we change our validateField function:

    function validateField(fieldValue = '', fieldConfig) {
      const specialProps = ['initialValue'];
      for (let validatorName in fieldConfig) {
        if (specialProps.includes(validatorName)) {
          continue;
        }
        // as before
      }
    }
    

    As we keep on adding more features like this, we can add them to our specialProps array.

    • See CodeSandbox demo

    Summing Up

    We’re well on our way to create an amazing validation library. We’ve added tons of features, and we’re pretty much-thought leaders by now.

    In the next part of this series, we’re going to add all of those extras that make our validation library even trend on LinkedIn. Stay tuned!

    (dm, yk, il)

    From our sponsors: Creating Your Own React Validation Library: The Features (Part 2)

    Posted on 23rd May 2019Web Design
    FacebookshareTwittertweetGoogle+share

    Related posts

    Archived
    22nd March 2023
    Archived
    18th March 2023
    Archived
    20th January 2023
    Thumbnail for 25788
    Handling Continuous Integration And Delivery With GitHub Actions
    19th October 2020
    Thumbnail for 25778
    A Monthly Update With New Guides And Community Resources
    19th October 2020
    Thumbnail for 25781
    Supercharge Testing React Applications With Wallaby.js
    19th October 2020
    Latest News
    • Archived
      22nd March 2023
    • Archived
      18th March 2023
    • Archived
      20th January 2023
    • 20201019 ML Brief
      19th October 2020
    • Thumbnail for 25788
      Handling Continuous Integration And Delivery With GitHub Actions
      19th October 2020
    • Thumbnail for 25786
      The Future of CX with Larry Ellison
      19th October 2020
    News Categories
    • Digital Marketing
    • Web Design

    Our services

    Website Design
    Website Design

    A website is an important part of any business. Professional website development is an essential element of a successful online business.

    We provide website design services for every type of website imaginable. We supply brochure websites, E-commerce websites, bespoke website design, custom website development and a range of website applications. We love developing websites, come and talk to us about your project and we will tailor make a solution to match your requirements.

    You can contact us by phone, email or send us a request through our online form and we can give you a call back.

    More Information

    Digital Marketing
    Digital Marketing

    Our digital marketeers have years of experience in developing and excuting digital marketing strategies. We can help you promote your business online with the most effective methods to achieve the greatest return for your marketing budget. We offer a full service with includes the following:

    1. Social Media Marketing

    2. Email & Newsletter Advertising

    3. PPC - Pay Per Click

    4. A range of other methods are available

    More Information

    SEO
    SEO Services

    SEO is an essential part of owning an online property. The higher up the search engines that your website appears, the more visitors you will have and therefore the greater the potential for more business and increased profits.

    We offer a range of SEO services and packages. Our packages are very popular due to the expanse of on-page and off-page SEO services that they cover. Contact us to discuss your website and the SEO services that would best suit to increase your websites ranking.

    More Information

    E-commerce
    E-commerce Websites

    E-commerce is a rapidly growing area with sales online increasing year on year. A professional E-commerce store online is essential to increase sales and is a reflection of your business to potential customers. We provide professional E-commerce websites custom built to meet our clients requirements.

    Starting to sell online can be a daunting task and we are here to make that journey as smooth as possible. When you work with Cunningham Web Solutions on your E-commerce website, you will benefit from the experience of our team and every detail from the website design to stock management is carefully planned and designed with you in mind.

    More Information

    Social Media Services
    Social Media Services

    Social Media is becoming an increasingly effective method of marketing online. The opportunities that social media marketing can offer are endless and when managed correctly can bring great benefits to every business.

    Social Media Marketing is a low cost form of advertising that continues to bring a very good ROI for our clients. In conjuction with excellent website development and SEO, social media marketing should be an essential part of every digital marketing strategy.

    We offer Social Media Management packages and we also offer Social Media Training to individuals and to companies. Contact us to find out more.

    More Information

    Cunningham Web Solutions
    © Copyright 2025 | Cunningham Web Solutions
    • Home
    • Our Services
    • FAQ's
    • Account Services
    • Privacy Policy
    • Contact Us