How to Migrate Redux to the Context API

How to Migrate Redux to the Context API

Redux shopping cart refactor to the Context API and React Hooks

In the following documentation, we are going to, from the ground up, refactor a Redux shopping cart product’s data into the Context Provider pattern.

This guide’s primary focus will be on comparing and contrasting Redux vs. the Context API.

The refactor aims to provide a guided example, as well as deepen our understanding of state management in React, implementing various efficient scalable patterns.

Process Overview

The shopping cart application will be pulled from the official Redux GitHub repository to maintain a standard example environment.

We will trace the Flux-like pattern of Redux in the shopping cart app, and by focusing on the product data, implement a successful state management refactor by leveraging React Hooks and the Context API.

Guide Outline
  • Clone Redux.
  • Scalable state management analysis.
  • Tracing shopping cart product data.
  • Reducers and Actions.
  • Thunk and logger: middleware vs. dispatch.
  • Implement Context Provider pattern.
  • HOCs, HOFs, and Redux Connect().
  • Leveraging React Hooks and useContext.
  • setTimeout() vs. promises.
  • Final Context Provider pattern refactor.

Alright. So, let’s grab some coffee and get this show on the road.

Please note that a basic level requirement of Node and React is beneficial for getting started and for general concept comprehension.

Clone Redux

First off, go to the Redux GitHub repository.

Redux Github Repo

Clone the source project by copying the SSH link and running git clonein the command prompt.

git clone [email protected]:reduxjs/redux.git

Copy the Shopping Cart folder which can be found in the folder directory: examples/shopping-cart and paste it somewhere where you can conveniently access it throughout this guide.

Now, using the terminal (Mac) command prompt, go cd shopping-cart into the shopping-cart folder directory and install all required node modules simply by running npm install.

Open the code source into your text editor and then launch the local development server by running npm start. We should now see the shopping cart application up and running in the browser.

Open up the DevTools Console (Chrome) to verify this and also note Redux Logger is activated and working.

Excellent. Now, let’s take a quick yet savory sip of coffee before proceeding.

Application Structure and API Emulation

Simply by viewing the shopping cart’s display in the browser, we can ascertain two main sections: the products section and the cart section.

  • The products section contains a list of products available to purchase including the title, price, and quantity. As well, there is an add to cart button to individually select products for the cart.
  • When we click on the iPad 4 Mini add to cart button, we see that the cart sectiongets updated, including the total price. If we scroll down to the Logger in the DevTools and check on the previous state vs. the next state, the products inventory is updated and reflected accordingly.

Next State Logger

But where are we receiving our product’s item data to begin with?

Good question. In the Redux shopping cart project’s API folder, we see a products.json file with a list of items and a shop.jsfile, grabbing and exporting the array from products.json.

The shop.js getProducts object’s property has an additional setTimeout function set to 100 milliseconds to simulate an Async-esque operation of fetching the shopping cart items from a real-world API scenario.

const TIMEOUT = 100

export default { getProducts: (cb, timeout) => setTimeout(() => cb(_products), timeout || TIMEOUT),

buyProducts: (payload, cb, timeout) => setTimeout(() => cb(), timeout || TIMEOUT)}

If we change the TIMEOUT const numerical value to 2000 (two seconds) and go back to the browser and refresh, we’ll notice the initial products render will now take two full seconds before displaying: const TIMEOUT = 2000.

Having now located and assessed our API data structure and retrieval setup, let’s fully trace the state retrieval management process of the products data in Redux.

Products: Actions, Reducers, and Thunk Middleware

If we go into the actions folder, the index.jscontains the following code:

import shop from '../api/shop'

import * as types from '../constants/ActionTypes'

const receiveProducts = products => ({

type: types.RECEIVE_PRODUCTS,

products})

export const getAllProducts = () => dispatch => {

shop.getProducts(products => {

dispatch(receiveProducts(products))

})}

The receiveProducts establishes the type types.RECEIVE_PRODUCTS and a payload of products.

This type is set up as const in the ActionTypes file. It’s then passed to a switch statement in two reducers, visibleIds and byIds, located in the reducer folder in products.js.

Maintaining focus on the actions, also notice a getAllProducts function which returns a dispatch function that grabs the products.json from the shop and sends the data payload of products into our receiveProducts action.

Back in the main index.jsfile located in our src folder, if we remove our Thunk middleware: const middleware = [ ];, we receive the following error:

Since the action performs an emulated real-life API fetch, set to a setTimeout(), we need to implement Thunk to correctly process and handle the async action.

Have a contemplative sip or two of coffee and return Thunk back to the middleware: const middleware = [thunk].

We’ve now managed to complete a full assessment of the product’s data API display: How and where we’re receiving the data and managing it in our application.

With this assessment, let’s proceed by setting up the architecture for the Context Provider pattern.

Context Provider Pattern Setup

Back in the project’s src folder, create a new folder called providers and file inside of the folder named products.provider.js.

In the products provider file, we’re going to set up a products provider pre-test demonstration with the following code:

import React,{createContext, useState} from 'react'

export const ProductsContext = createContext({
    test: ''
})

const ProductsProvider = ({children}) => {
    const [test] = useState('the products provider has been successfully connected :)')
    return (
        <ProductsContext.Provider value={{test}}>
            {children}
        </ProductsContext.Provider>
    )
}

export default ProductsProvider

The code above first brings in createContext to access the React Context API and the useState Hook. Then, we set our context to the constProductsContext where we initialize an object that takes the property of test which we set to an empty string.

After that, we create the ProductsProviderfunction which takes the object of children props as its parameter, passing the props of children through the Context.Provider.

Within, we initialize the state of test to a string: const [test] = useState(‘the products provider has been successfully connected :)’).

ProductsProvider then explicitly returns the ProductsContext to the Provider with a value of the object test passing in the state with the property of children.

Alright, let’s stop for a second. Might sound like a bit of mouthful, but it’s actually quite simple, except that it takes a bit of following in terms of how things are hooked up.

If you’re having trouble following, go back and take each step one at a time, slowly. Just make sure you follow the traces and it will make much more sense, otherwise, if you’ve managed to follow along up to this part, kudos and let’s keep moving!

The products Context is made aware of an empty string of test in which we initialize state by setting up a const test in the product’s Provider, also taking a string.

We can then set the state of test to the object property of test by setting the ProductsContext.Provider value={{test}} to an object receiving test.

Finally, pass the children over as a wrapper.

To enable the Provider to be wrapped around the component/container of our choosing, let’s simply go into the index.js filein thesrc folder, import the Provider, and wrap it around our application granting it access to the children.

import React from 'react'
import { render } from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import { createLogger } from 'redux-logger'
import thunk from 'redux-thunk'
import reducer from './reducers'
import { getAllProducts } from './actions'
import App from './containers/App'
import ProductsProvider from './provider/products.provider'

const middleware = [thunk];
if (process.env.NODE_ENV !== 'production') {
  middleware.push(createLogger());
}

const store = createStore(
  reducer,
  applyMiddleware(...middleware)
)

store.dispatch(getAllProducts())

render(
  <ProductsProvider>
  <Provider store={store}>
    <App />
  </Provider>
  </ProductsProvider>,
  document.getElementById('root')
)

Note that the ProductsProvider wraps over the Redux store Provider illustrating it at the utmost top of our application’s state management tree.

Context Provider Pattern Test

Time to drink some more coffee and test if our new Context Provider is effectively working.

Go into the containers folder in productsContainer.js.We can now bring in the useContext Hook and destructure our test from the ProductsContext into our products container to see if it works.

import React,{useContext} from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { addToCart } from '../actions'
import { getVisibleProducts } from '../reducers/products'
import ProductItem from '../components/ProductItem'
import ProductsList from '../components/ProductsList'
import {ProductsContext} from '../provider/products.provider'

const ProductsContainer = ({ products, addToCart }) => {
  const {test} = useContext(ProductsContext)
  console.log(test)
return (
  <ProductsList title="Products">
    {products.map(product =>
      <ProductItem
        key={product.id}
        product={product}
        onAddToCartClicked={() => addToCart(product.id)} />
    )}
  </ProductsList>
)
    }
ProductsContainer.propTypes = {
  products: PropTypes.arrayOf(PropTypes.shape({
    id: PropTypes.number.isRequired,
    title: PropTypes.string.isRequired,
    price: PropTypes.number.isRequired,
    inventory: PropTypes.number.isRequired
  })).isRequired,
  addToCart: PropTypes.func.isRequired
}

const mapStateToProps = state => ({
  products: getVisibleProducts(state.products)
})

export default connect(
  mapStateToProps,
  { addToCart }
)(ProductsContainer)

Our productsContainer.jsshould now be updated to the code above. Upon browser refresh, DevTools now displays the test log successfully.

Very nice. Our Context Provider pattern for our product’s display is now fully connected and ready for implementation.

Let’s head back to our products.provider.js file and completely refactor the product’s data into our Context API setup.

Migrate Products Data to Context Provider

We’ll now update the products.provider.jscode. First, we’ll import the shop from our shop.js.

We’ll establish a new property in our context object for the products’ data and set it to an empty array. We’ll then import the useEffect Hook as well from React and create a products’ state also set to an empty array.

Then, we’ll leverage the useEffect Hook to render our product’s data by setting the hook to async to await grabbing the product’s data from our shop and setting the response to our products state.

We’ll leave an empty array in our useEffect so that the mounting life-cycle executes once, by default.

Finally, we’ll bring the product’s state into the Context Provider’s value object.

import React,{createContext, useState, useEffect} from 'react'
import shop from '../api/shop'

export const ProductsContext = createContext({
    test: '',
    products: []
})

const ProductsProvider = ({children}) => {
    const [test] = useState('the products provider has been successfully connected :)')
    const [products, setProducts] = useState([])
    useEffect(async ()=> {
        const response = await shop.getProducts(products => products)
        setProducts(response)
},[])
    return (
        <ProductsContext.Provider value={{test, products}}>
            {children}
        </ProductsContext.Provider>
    )
}

export default ProductsProvider
Final Implementation and Async vs. Promises

Saving the newly updated code, let’s refill our coffee cups and head back to the productsContainer.js file.

Let’s update our products’ data to be called from our Products Provider instead of our Redux Provider by destructuring products off the ProductsContext and removing products destructured props from the ProductsContainer, as shown below.

import React,{useContext} from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { addToCart } from '../actions'
import { getVisibleProducts } from '../reducers/products'
import ProductItem from '../components/ProductItem'
import ProductsList from '../components/ProductsList'
import {ProductsContext} from '../provider/products.provider'

const ProductsContainer = ({  addToCart }) => {
  const {test, products} = useContext(ProductsContext)
  console.log(test)
return (
  <ProductsList title="Products">
    {products.map(product =>
      <ProductItem
        key={product.id}
        product={product}
        onAddToCartClicked={() => addToCart(product.id)} />
    )}
  </ProductsList>
)
    }
ProductsContainer.propTypes = {
  products: PropTypes.arrayOf(PropTypes.shape({
    id: PropTypes.number.isRequired,
    title: PropTypes.string.isRequired,
    price: PropTypes.number.isRequired,
    inventory: PropTypes.number.isRequired
  })).isRequired,
  addToCart: PropTypes.func.isRequired
}

const mapStateToProps = state => ({
  products: getVisibleProducts(state.products)
})

export default connect(
  mapStateToProps,
  { addToCart }
)(ProductsContainer)

After saving everything, we will now run into the following error.

No need to panic, this is expected behavior. Since setTimeout does not return a promise, async await will not execute accordingly to prevent JavaScript from running until the setTimeout interval value is completed.

To maintain the coded simulation effect of this API, let’s go back to the shop.jsfile and promisify the code.

Let’s create an async anonymous function and wrap a new Promise around the getProduct data retrieval property.

* Mocking client-server processing */

import _products from './products.json'

const TIMEOUT = 100

export default {

getProducts: async ()=> new Promise((cb, timeout) => setTimeout(() => cb(_products), timeout || TIMEOUT)),

buyProducts: (payload, cb, timeout) => setTimeout(() => cb(), timeout || TIMEOUT)
}

With the new promise patch modification, getProducts will now return a promise for our async useEffect to retrieve.

Saving this latest update, our product’s shopping cart data will once again successfully be displayed upon mounting.

Congratulations. We have now migrated our initial product’s data display from Redux into the newly instated Context Provider pattern.

Take a congratulatory sip or three of coffee and let’s do some final code clean-up and review.

Final Refactor and Conclusion

Back in our productsContainer.js file, we can delete our mapStateToProps const and remove it from our connect since our products’ data retrieval is no longer being managed by Redux.

export default connect(null,{ addToCart })(ProductsContainer)

Our application will continue to work as is it did before, demonstrating a successful refactor.

Just like Connect() is a higher-order component that wrapped around our productsContainer component to pass over the data state to props, our ProductsProvider now acts in its place.

The Products Context passes the children props of the products data state as the Products Provider wraps around the main application in our index.jsby being placed at the top of the app tree.

Although both implementations are effective, this guide in no way favors one over the other as an ultimate go-to.

It simply depends on each application and these are the decisions we need to think about carefully and make to achieve the most effective state management path for our applications.

This guide has purely been intended to simulate a refactor process and analysis of working with Context and Redux as a basic starting point.

If you’d like an additional exercise idea, you can go ahead and find something else in the Redux shopping cart example to refactor.

If you have any questions or comments, please feel free to leave them below. You can also check out the full source code or video tutorial in the links at the top of this post.

Thank you for checking this out and I hope you found some of this helpful!

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

What’s new in HTML6

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

React Hooks Tutorial for Beginners: Getting Started With React Hooks

React Hooks Tutorial for Beginners: Getting Started With React Hooks

React hooks tutorial for beginners, learn React hooks step by step: Introduction, useState Hook, useState with previous state, useState with object, useState with array, useEffect Hook, useEffect after render, Conditionally run effects, Run effects only once, useEffect with cleanup, useEffect with incorrect dependency, Fetching data with useEffect, useContext Hook, useReducer Hook, useReducer, Multiple useReducers, useReducer with useContext, Fetching data with useReducer, useState vs useReducer, useCallback Hook, useMemo Hook, useRef Hook

React Hooks Tutorial - 1 - Introduction

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.

React Hooks Tutorial - 2 - useState Hook React Hooks Tutorial - 3 - useState with previous state React Hooks Tutorial - 4 - useState with object React Hooks Tutorial - 5 - useState with array React Hooks Tutorial - 6 - useEffect Hook React Hooks Tutorial - 7 - useEffect after render React Hooks Tutorial - 8 - Conditionally run effects React Hooks Tutorial - 9 - Run effects only once React Hooks Tutorial - 10 - useEffect with cleanup React Hooks Tutorial - 11 - useEffect with incorrect dependency React Hooks Tutorial - 12 - Fetching data with useEffect Part 1 React Hooks Tutorial - 13 - Fetching data with useEffect Part 2 React Hooks Tutorial - 14 - Fetching data with useEffect Part 3 React Hooks Tutorial - 15 - useContext Hook Part 1 React Hooks Tutorial - 16 - useContext Hook Part 2 React Hooks Tutorial - 17 - useContext Hook Part 3 React Hooks Tutorial - 18 - useReducer Hook React Hooks Tutorial - 19 - useReducer (simple state & action) React Hooks Tutorial - 20 - useReducer (complex state & action) React Hooks Tutorial - 21 - Multiple useReducers React Hooks Tutorial - 22 - useReducer with useContext React Hooks Tutorial - 23 - Fetching data with useReducer Part 1 React Hooks Tutorial - 24 - Fetching data with useReducer Part 2 React Hooks Tutorial - 25 - useState vs useReducer React Hooks Tutorial - 26 - useCallback Hook React Hooks Tutorial - 27 - useMemo Hook React Hooks Tutorial - 28 - useRef Hook Part 1 React Hooks Tutorial - 29 - useRef Hook Part 2 React Hooks Tutorial - 30 - Custom Hooks React Hooks Tutorial - 31 - useDocumentTitle Custom Hook React Hooks Tutorial - 32 - useCounter Custom Hook React Hooks Tutorial - 33 - useInput Custom Hook

Why React Hooks + Context API a better choice than React + Redux

Why React Hooks + Context API a better choice than React + Redux

In this React tutorial, I will introduce the React Context API for state management, and show you what makes React Hooks plus the Context API a better solution than React plus Redux

Redux introduces a lot of complexity to our codebase with the excessive amount of code it requires. At best, this makes it an imperfect solution for state management in React applications. And yet, far too many React developers default to Redux for state management without considering other alternatives.

In this article, I will introduce the React Context API for state management, and show you what makes React Hooks plus the Context API a better solution than Redux.

Why we need a state management tool

In typical React, the way to handle data between disconnected components is through prop drilling. Since there is no global state that components can access if, for instance, you want to pass data from a top-level component to a fifth-level component, you’ll have to pass the data as a prop on each level of the tree until you get to your desired component.

This results in writing a ton of extra code, and giving components properties that they will never use also affects their architectural design. In order to solve this problem, we needed a way to provide a global state that all components, no matter how deeply nested they are, could access.

By solving this, Redux, an open-source JavaScript library for managing application state, became the go-to solution for React developers.

How Redux works

The Redux documentation describes it as a predictable state container for JavaScript applications that helps us to write applications that behave consistently, run in different environments, and are easy to test.

One disadvantage of prop drilling is the need for writing a considerable amount of extra code in order to access data from a top-level component. With Redux, this disadvantage is felt even more as a lot of complexity comes with all its extra code required for setting up a global state for our application. Redux requires three main building parts to function: actions, reducers, and store.

Actions

These are objects that are used to send data to the Redux store. They typically have two properties: a type property for describing what the action does and a payload property that contains the information that should be changed in the app state.

// action.js
const reduxAction = payload => {
  return {
    type: 'action description',
    payload
  }
};

export default reduxAction;

The type is usually in all caps, with its words separated by underscores. For example, SIGNUP_USER or DELETE_USER_DATA.

Reducers

These are pure functions that implement the action behavior. They take the current application state, perform an action, and then return a new state:

const reducer = (state, action) => {
  const { type, payload } = action;
  switch(type){
    case "action type":
      return {
        ["action description"]: payload
      };
    default:
      return state;
  }
};

export default reducer;

Store

The store is where the application’s state is housed. There is only one store in any Redux application:

import { createStore } from 'redux'

const store = createStore(componentName);

Since our application can only have one Redux store, in order to create a single root reducer for all our components, we’ll need the [combineReducers](https://redux.js.org/api/combinereducers) method from Redux.

With this long process and considerable amount of code required to set up Redux, imagine what our codebase will look like when we have multiple components to work with. Even though Redux solves our state management problem, it is really time-consuming to use, has a difficult learning curve, and introduces a whole new layer of complexity to our application.

Fortunately, the React Context API solves this problem. When combined with React Hooks, we have a state management solution that is less time-consuming to set up, has an easy learning curve, and requires minimal code.

The React Context API

The new Context API came with React 16.3. Here’s how Context is explained in the React documentation:

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

The React context API is React’s way of managing state in multiple components that are not directly connected.

To create a context, we’ll use the createContext method from React, which accepts a parameter for its default value:

import React from 'react';

const newContext = React.createContext({ color: 'black' });

The createContext method returns an object with a Provider and a Consumer component:

const { Provider, Consumer } = newContext;

The Provider component is what makes the state available to all child components, no matter how deeply nested they are within the component hierarchy. The Provider component receives a value prop. This is where we’ll pass our current value:

<Provider value={color: 'blue'}>
  {children}
</Provider>

The Consumer, as its name implies, consumes the data from the Provider without any need for prop drilling:

<Consumer>
  {value => <span>{value}</span>}}
</Consumer>

Without Hooks, the Context API might not seem like much when compared to Redux, but combined with the useReducer Hook, we have a solution that finally solves the state management problem.

What are Hooks in React?

Hooks are a type of function that enables the execution of custom code in a base code. In React, Hooks are special functions that allow us to “hook into” its core features.

React Hooks provide an alternative to writing class-based components by allowing us to easily handle state management from functional components.

The useContext Hook

If you noticed, when explaining the React Context API, we needed to wrap our content in a Consumer component and then pass a function as a child just so we could access (or consume) our state. This introduces unnecessary component nesting and increases the complexity of our code.

The useContext Hook makes things a lot nicer and straightforward. In order to access our state using it, all we need to do is call it with our created context as its argument:

const newContext = React.createContext({ color: 'black' });

const value = useContext(newContext);

console.log(value); // this will return { color: 'black' }

Now, instead of wrapping our content in a Consumer component, we can simply access our state through the value variable.

The useReducer Hook

The useReducer Hook came with React 16.7.0. Just like the reduce() method in JavaScript, the useReducer Hook receives two values as its argument — in this case, the current state and an action — and then returns a new state:

const [state, dispatch] = useReducer((state, action) => {
  const { type } = action;
  switch(action) {
    case 'action description':
      const newState = // do something with the action
      return newState;
    default:
      throw new Error()
  }
}, []);

In the above block, we’ve defined our state and a corresponding method, dispatch, handling it. When we call the dispatch method, the useReducer() Hook will perform an action based on the type that our method receives in its action argument:

...
return (
  <button onClick={() =>
    dispatch({ type: 'action type'})}>
  </button>
)
The useReducer Hook plus the Context API

Setting up our store

Now that we know how the Context API and the useReducer Hook work individually, let’s see what happens when we combine them in order to get the ideal global state management solution for our application. We’ll create our global state in a store.js file:

// store.js
import React, {createContext, useReducer} from 'react';

const initialState = {};
const store = createContext(initialState);
const { Provider } = store;

const StateProvider = ( { children } ) => {
  const [state, dispatch] = useReducer((state, action) => {
    switch(action.type) {
      case 'action description':
        const newState = // do something with the action
        return newState;
      default:
        throw new Error();
    };
  }, initialState);

  return <Provider value={{ state, dispatch }}>{children}</Provider>;
};

export { store, StateProvider }

In our store.js file, we used the createContext() method from React that we explained earlier to create a new context. Remember that the createContext() method returns an object with a Provider and Consumer component. This time, we’ll be using only the Provider component and then the useContext Hook when we need to access our state.

Notice how we used the useReducer Hook in our StateProvider. When we need to manipulate our state, we’ll call the dispatch method and pass in an object with the desired type as its argument.

In our StateProvider, we returned our Provider component with a value prop of state and dispatch from the useReducer Hook.

Accessing our state globally

In order to access our state globally, we’ll need to wrap our root <App/> component in our StoreProvider before rendering it in our ReactDOM.render() function:

// root index.js file
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { StateProvider } from './store.js';

const app = (
  <StateProvider>
    <App />
  </StateProvider>
);

ReactDOM.render(app, document.getElementById('root'));

Now, our store context can be accessed from any component in the component tree. To do this, we’ll import the useContext Hook from react and the store from our ./store.js file:

// exampleComponent.js
import React, { useContext } from 'react';
import { store } from './store.js';

const ExampleComponent = () => {
  const globalState = useContext(store);
  console.log(globalState); // this will return { color: red }
};

Adding and removing data from our state

We’ve seen how we can access our global state. In order to add and remove data from our state, we’ll need the dispatch method from our store context. We only need to call the dispatch method and pass in an object with type (the action description as defined in our StateProvider component) as its parameter:

// exampleComponent.js
import React, { useContext } from 'react';
import { store } from './store.js';

const ExampleComponent = () => {
  const globalState = useContext(store);
  const { dispatch } = globalState;

  dispatch({ type: 'action description' })
};
Conclusion

To a good extent, Redux works for state management in React applications and has a few advantages, but its verbosity makes it really difficult to pick up, and the ton of extra code needed to get it working in our application introduces a lot of unnecessary complexity.

On the other hand, with the useContext API and React Hooks, there is no need to install external libraries or add a bunch of files and folders in order to get our app working. This makes it a much simpler, more straightforward way to handle global state management in React applications.

How to Build the Next Generation of Forms with React Hooks Forms

How to Build the Next Generation of Forms with React Hooks Forms

Performant, flexible, and extensible forms, easy to use with validation by React Hooks

Drag and Drop Builder. You can build your own form with auto-generated code here at the React Hook Form Website. You will be able to re-arrange using drag and drop, delete, and edit each field to start using this incredible plugin, without having to read any documentation, simply by copying that code.

Why Not Other React Library Forms?

It’s really simple, there are multiple good reasons:

  1. Easy to adopt as form state is inherently local, it can be easily adopted without other dependencies.

  2. Reduces the code to handle forms, with less complexity due to the Hooks. You can find a complete code comparison here.

  3. Performance is important and package size matters. This is a tiny library without any dependencies.

  4. Minimizes the number of re-renders and faster mount, striving to provide the best user experience. Twenty times less than other packages like Formik or Redux Form

Installation

Installing React Hook Form only takes a single command and you’re ready to roll. If you are using npm:

npm install react-hook-form

Or, if you are using Yarn:

yarn add react-hook-form
Basic Usage

The following code will demonstrate basic usage.

The main component used is the useForm Hook with the returned functions and variables:

  • register: To connect a field to rules or validation functions.
  • handleSubmit: To check and validate all fields before sending submit.
  • watch: This will watch specified inputs and return their value.
  • errors: Contains form errors or error messages that belong to each input.
import React from 'react'
import useForm from 'react-hook-form'

export default function App() {
  const { register, handleSubmit, watch, errors } = useForm()
  const onSubmit = data => { console.log(data) }

  console.log(watch('example')) // watch input value by passing the name of it

  return (
    {/* "handleSubmit" will validate your inputs before invoking "onSubmit" */}
    <form onSubmit={handleSubmit(onSubmit)}>
    {/* register your input into the hook by invoking the "register" function */}
      <input name="example" defaultValue="test" ref={register} />
      
      {/* include validation with required or other standard HTML validation rules */}
      <input name="exampleRequired" ref={register({ required: true })} />
      {/* errors will return when field validation fails  */}
      {errors.exampleRequired && <span>This field is required</span>}
      
      <input type="submit" />
    </form>
  )
}
Register Fields

Each field needs a specific unique name that you will also use for labels and then you will pass the register Hook to the ref element.

Inside it, you will pass multiple parameters. Required is needed to tell if the user has to enter that field and then you will be able to easily use other standard HTML rules.

Here’s the list of validation rules supported:

  • required
  • min
  • max
  • minLength
  • maxLength
  • pattern
  • validate
import React from 'react'
import useForm from 'react-hook-form'

export default function App() {
  const { register, handleSubmit } = useForm()
  const onSubmit = data => console.log(data)
   
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={register({ required: true, maxlength: 20 })} />
      <input name="lastName" ref={register({ pattern: /^[A-Za-z]+$/i })} />
      <input name="age" type="number" ref={register({ min: 18, max: 99 })} />
      <input type="submit" />
    </form>
  );
}

The validate function is a great way to use custom logic specific to that field, enabling you to implement custom behavior very easily.

<input
  name="single"
  ref={
    register({
      validate: (value) => value === '1'
    })
  }
/>
Handle Fields Errors

React Hook Form provides an errors object to show you the errors within the form related to each unique field.

import React from 'react'
import useForm from 'react-hook-form'

export default function App() {
  const { register, errors } = useForm()
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Input name="firstName" ref={register({ required: true })} />
      {errors.firstName && 'First name is required'}
      <Input name="lastName" ref={register({ required: true })} />
      {errors.lastName && 'Last name is required'}
      <input type="submit" />
    </form>
  );
}
Drag and Drop Builder

You can build your own form with auto-generated code here at the React Hook Form Website.

You will be able to re-arrange using drag and drop, delete, and edit each field to start using this incredible plugin, without having to read any documentation, simply by copying that code.

You can easily build a form like this one in less than one minute!

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

export default function App() {
  const { register, handleSubmit, errors } = useForm();
  const onSubmit = data => console.log(data);
  console.log(errors);
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input type="text" placeholder="First name" name="First name" ref={register({required: true, maxLength: 80})} />
      <input type="text" placeholder="Last name" name="Last name" ref={register({required: true, maxLength: 100})} />
      <input type="text" placeholder="Email" name="Email" ref={register({required: true, pattern: /^\[email protected]\S+$/i})} />
      <input type="tel" placeholder="Mobile number" name="Mobile number" ref={register({required: true, minLength: 6, maxLength: 12})} />
      <select name="Title" ref={register({ required: true })}>
        <option value="Mr">Mr</option>
        <option value="Mrs">Mrs</option>
        <option value="Miss">Miss</option>
        <option value="Dr">Dr</option>
      </select>

      <input name="Developer" type="radio" value="Yes" ref={register({ required: true })}/>
      <input name="Developer" type="radio" value="No" ref={register({ required: true })}/>

      <input type="submit" />
    </form>
  );
}

I hope this tutorial will surely help and you if you liked this tutorial, please consider sharing it with others. Thank you!