React Tips — Test Buttons, Form Validation, and 404 Routes

John Au-Yeung - Jan 21 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

React is a popular library for creating web apps and mobile apps.

In this article, we’ll look at some tips for writing better React apps.

Simulate a Button Click in Jest

To simulate a button click with Jest, we can write:

import React from 'react';
import { shallow } from 'enzyme';
import Button from './Button';

describe('Test button', () => {
  it('Test click event', () => {
    const mockOnClick = jest.fn();
    const button = shallow((<Button onClick={mockOnClick}>click me</Button>));
    button.find('button').simulate('click');
    expect(mockOnClick.mock.calls.length).toEqual(1);
  });
});
Enter fullscreen mode Exit fullscreen mode

We create a mock function with and pass it into our button component.

Then we get the button and call simulate with the argument 'click' do simulate a click.

Then we can check if the mockOnClick function is called at the end.

Alternatively, we can write similar code in a Sinon test.

For instance, we can write:

import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';

import Button from './Button';

describe('Test Button component', () => {
  it('simulates click events', () => {
    const mockOnClick = sinon.spy();
    const button = shallow((<Button onClick={mockOnClick}>click me</Button>));

    button.find('button').simulate('click');
    expect(mockOnClick).toHaveProperty('callCount', 1);
  });
});
Enter fullscreen mode Exit fullscreen mode

We create a mock function with sinon.spy() .

Then we pass that into the onClick callback.

And then we call simulate with the 'click' argument to simulate the click.

We can also call our onClick function from the props directly.

For example, we can write:

wrapper.find('Button').prop('onClick')()
Enter fullscreen mode Exit fullscreen mode

How to Add Multiple Middlewares to Redux

We can add multiple middlewares to Redux with the applyMiddleware method.

For example, we can write:

const createStoreWithMiddleware = applyMiddleware(ReduxThunk, logger)(createStore);
Enter fullscreen mode Exit fullscreen mode

We just pass them all into the applyMiddleware function directly.

Also, we can put them all into an array and spread them into applyMiddleware :

const middlewares = [ReduxThunk, logger];
applyMiddleware(...middlewares)
Enter fullscreen mode Exit fullscreen mode

Callback When DOM is Loaded in React

componentDidMount is the callback that’s called when the DOM is loaded.

For instance, we can write:

class Comp1 extends React.Component {
  constructor(props) {
    super(props);
    this.handleLoad = this.handleLoad.bind(this);
  }

  componentDidMount() {
    window.addEventListener('load', this.handleLoad);
  }

  componentWillUnmount() {
    window.removeEventListener('load', this.handleLoad)
  }

  handleLoad() {
    //...
  }

  render() {
    //...
  }
}
Enter fullscreen mode Exit fullscreen mode

We have the handleLoad method that we added as the listener fo the load event in componentDidMount .

In componentWillUnmount , we remove the event listener when the component unloads.

Properly Validate Input Values with React

To make form validation easy for us, we can use the react-hook-form library.

For instance, we can write:

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

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstname" ref={register} />

      <input name="lastname" ref={register({ required: true })} />
      {errors.lastname && 'last name is required'} {

      <input name="age" ref={register({ pattern: /d+/ })} />
      {errors.age && 'invalid age.'}

      <input type="submit" />
    </form>
  )
}
Enter fullscreen mode Exit fullscreen mode

We use the useForm hook that comes with the package.

Then we can use the functions and objects that are returned with the hook to check for valid input and display error messages.

register lets us register the input field with react-hook-form and add the validation rules.

Registering the field will make it available when we run our submit handler.

We can pass an object with the validation rules as we did in the last name and age inputs.

We applied the required rule with the last name field.

And we set a regex for the valid format in the age field.

In the onSubmit function, we have the data parameter which has the filled-in values.

It’s only run if all the fields are valid.

errors have errors in a field.

Add a Not Found Page with React Router

We can add a not found page with React Router by adding a Router compoent with no path.

For instance, we can write:

<Switch>
  <Route exact path="/">
    <Home />
  </Route>
  <Route path="/will-match">
    <WillMatch />
  </Route>
  <Route path="*">
     <NoMatch />
  </Route>
</Switch>
Enter fullscreen mode Exit fullscreen mode

The last entry:

<Route path="*">
  <NoMatch />
</Route>
Enter fullscreen mode Exit fullscreen mode

is the wildcard route that’s loaded when the URL doesn’t match anything else listed.

Conclusion

We can create a no matching route with React Router.

We can simulate a button click with a mock function.

To make form validation easy, we should use a package.

And we can add multiple middlewares with React-Redux.
React is a popular library for creating web apps and mobile apps.

In this article, we’ll look at some tips for writing better React apps.

Simulate a Button Click in Jest

To simulate a button click with Jest, we can write:

import React from 'react';
import { shallow } from 'enzyme';
import Button from './Button';

describe('Test button', () => {
  it('Test click event', () => {
    const mockOnClick = jest.fn();
    const button = shallow((<Button onClick={mockOnClick}>click me</Button>));
    button.find('button').simulate('click');
    expect(mockOnClick.mock.calls.length).toEqual(1);
  });
});
Enter fullscreen mode Exit fullscreen mode

We create a mock function with and pass it into our button component.

Then we get the button and call simulate with the argument 'click' do simulate a click.

Then we can check if the mockOnClick function is called at the end.

Alternatively, we can write similar code in a Sinon test.

For instance, we can write:

import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';

import Button from './Button';

describe('Test Button component', () => {
  it('simulates click events', () => {
    const mockOnClick = sinon.spy();
    const button = shallow((<Button onClick={mockOnClick}>click me</Button>));

    button.find('button').simulate('click');
    expect(mockOnClick).toHaveProperty('callCount', 1);
  });
});
Enter fullscreen mode Exit fullscreen mode

We create a mock function with sinon.spy() .

Then we pass that into the onClick callback.

And then we call simulate with the 'click' argument to simulate the click.

We can also call our onClick function from the props directly.

For example, we can write:

wrapper.find('Button').prop('onClick')()
Enter fullscreen mode Exit fullscreen mode

How to Add Multiple Middlewares to Redux

We can add multiple middlewares to Redux with the applyMiddleware method.

For example, we can write:

const createStoreWithMiddleware = applyMiddleware(ReduxThunk, logger)(createStore);
Enter fullscreen mode Exit fullscreen mode

We just pass them all into the applyMiddleware function directly.

Also, we can put them all into an array and spread them into applyMiddleware :

const middlewares = [ReduxThunk, logger];
applyMiddleware(...middlewares)
Enter fullscreen mode Exit fullscreen mode

Callback When DOM is Loaded in React

componentDidMount is the callback that’s called when the DOM is loaded.

For instance, we can write:

class Comp1 extends React.Component {
  constructor(props) {
    super(props);
    this.handleLoad = this.handleLoad.bind(this);
  }

  componentDidMount() {
    window.addEventListener('load', this.handleLoad);
  }

  componentWillUnmount() {
    window.removeEventListener('load', this.handleLoad)
  }

  handleLoad() {
    //...
  }

  render() {
    //...
  }
}
Enter fullscreen mode Exit fullscreen mode

We have the handleLoad method that we added as the listener fo the load event in componentDidMount .

In componentWillUnmount , we remove the event listener when the component unloads.

Properly Validate Input Values with React

To make form validation easy for us, we can use the react-hook-form library.

For instance, we can write:

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

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstname" ref={register} />

      <input name="lastname" ref={register({ required: true })} />
      {errors.lastname && 'last name is required'} {

      <input name="age" ref={register({ pattern: /d+/ })} />
      {errors.age && 'invalid age.'}

      <input type="submit" />
    </form>
  )
}
Enter fullscreen mode Exit fullscreen mode

We use the useForm hook that comes with the package.

Then we can use the functions and objects that are returned with the hook to check for valid input and display error messages.

register lets us register the input field with react-hook-form and add the validation rules.

Registering the field will make it available when we run our submit handler.

We can pass an object with the validation rules as we did in the last name and age inputs.

We applied the required rule with the last name field.

And we set a regex for the valid format in the age field.

In the onSubmit function, we have the data parameter which has the filled-in values.

It’s only run if all the fields are valid.

errors have errors in a field.

Add a Not Found Page with React Router

We can add a not found page with React Router by adding a Router compoent with no path.

For instance, we can write:

<Switch>
  <Route exact path="/">
    <Home />
  </Route>
  <Route path="/will-match">
    <WillMatch />
  </Route>
  <Route path="*">
     <NoMatch />
  </Route>
</Switch>
Enter fullscreen mode Exit fullscreen mode

The last entry:

<Route path="*">
  <NoMatch />
</Route>
Enter fullscreen mode Exit fullscreen mode

is the wildcard route that’s loaded when the URL doesn’t match anything else listed.

Conclusion

We can create a no matching route with React Router.

We can simulate a button click with a mock function.

To make form validation easy, we should use a package.

And we can add multiple middlewares with React-Redux.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .