How to use onClick, onMouseOver and async events in React

1st August 2021

There are many similarities when using setting up React events, such as onClick and onMouseOver.

We will first look at the button onClick event, and how to bind an event handler to it.

Afterwards, we will learn how to set up an event handler from another Razor functional component.

From there, we will look at onMouseOver and onMouseOut events. This will help us to change the background colour when we hover our mouse over a certain element.

Finally, we will look at how to set up async event handlers.

Using a React 'notes' application

We are going to use a notes app to demonstrate these React events.

This application has been created as a React TypeScript app. There are many benefits with using TypeScript, including being able to define strongly typed variables, which leads to better error handling.

Eagle-eyed readers will realise that this is the same application as we used when looking at the button onclick event in Blazor WebAssembly.

We begin with a Note class. The Note class stores the note's message and when the created date.

/* Note.tsx */
class Note {
    Message: string;
    Created: Date;

    constructor(message: string) {
        this.Message = message;
        this.Created = new Date();
    }
}
export default Note;

From there, we set up two Razor functional components.

The first is NoteViewComponent, and this will display the note's message, and the created date. We have set up a NoteViewComponentProps interface and this has a Note instance. This interface will be used as the NoteViewComponent props instance.

/* NoteViewComponent.tsx */
import React from "react";
import Note from "../note/note";
import './note-view-component.css'

interface NoteViewComponentProps {
    Note: Note;
}

const NoteViewComponent: React.FC<NoteViewComponentProps> = (props) => {

    return <li>
    <span>{props.Note.Message}</span>
    <span>Created: {props.Note.Created.toString()}</span>
    </li>
}

export default NoteViewComponent;

The second is NoteListingComponent. This will display all the created notes, and allows the user to create a new note.

We have set up a NoteListingComponentProps interface. This interface will store all the Notes as an array. It will then use that array to loop through all the notes, and pass the Note instance to a NoteViewComponent instance.

/* NoteListingComponent.tsx */
import React, { useState } from "react";
import Note from "../note/note";
import NoteViewComponent from "./note-view-component";
import './note-listing-component.css'

interface NoteListingComponentProps {
    Notes: Note[];
}
const NoteListingComponent: React.FC<NoteListingComponentProps> = (props) => {
   
    const [notes, setNotes] = useState(props.Notes);

    return <>
        <div className="col-6">
        <h2>Enter your note</h2>
        <fieldset>
            <label htmlFor="Comment">
                <textarea id="Comment" cols={50} rows={6}></textarea>
            </label>
        </fieldset>
        <button type="submit">Submit</button>
        </div>
        <div className="col-6">
            <h2>Your saved notes</h2>
            {notes && notes.length > 0 ? (
            <ul>
                {notes.map((note: Note, index: number) => {
                    return <NoteViewComponent key={`Note`+index} Note={note}></NoteViewComponent>
                })}
            </ul>    
            ) : (
                <p>You currently do not have any saved notes.</p>
            )}
        </div>
    </>
}
export default NoteListingComponent;

Adding a button onClick event

Our first task is to add a button onClick event, and we will use this so we are able to add a note to our app.

What we will do is create a newNote string type. Using React's useState hook, we create a pair of values. The first is newNote which is the variable that stores the message. The other is setNewNote which is a function that you can call to update the newNote variable.

When calling the function stored in useState, it updates the functional component, assuming that the new value is different from the old value.

/* NoteListingComponent.tsx */
...
const NoteListingComponent: React.FC<NoteListingComponentProps> = (props) => {
   
    ...
    const [newNote, setNewNote] = useState('');

	...
}
...

Afterwards, we need to set up a function. This function will help store the message when typing a new message. The newNote variable will be updated every time there is a keyboard input.

/* NoteListingComponent.tsx */
import React, { ChangeEvent, useState } from "react";
...

const NoteListingComponent: React.FC<NoteListingComponentProps> = (props) => {
   
    ...

    const onNoteChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
        if (event && event.target) {
            setNewNote(event.target.value);
        }   
    }

    ...
}
...

Finally, we need to bind our onNoteChange function to the React onChange event as an event handler in our text box. This will mean that when the onChange event is fired, it will execute the onNoteChange function to update the newNote instance.

/* NoteListingComponent.tsx */
...
const NoteListingComponent: React.FC<NoteListingComponentProps> = (props) => {
   
	...

    return <>
        ...
        <fieldset>
            <label htmlFor="Comment">
                <textarea id="Comment" cols={50} rows={6} onChange={onNoteChange} value={newNote}></textarea>
            </label>
        </fieldset>
        ...
    </>
}
...

Now that we have the newNote variable that stores what we are typing in our notes text box, the next thing we need to do is to add the note's message to our notes list.

To do that, we set up an onCreateNote function. What this will do is add a new Note to our notes list, and then reset the value in our newNote variable. This will empty the text box when a new note is created.

/* NoteListingComponent.tsx */
import React, { ChangeEvent, MouseEvent, useState } from "react";
...

const NoteListingComponent: React.FC<NoteListingComponentProps> = (props) => {
   
    const [notes, setNotes] = useState(props.Notes);
    const [newNote, setNewNote] = useState('');

    const onCreateNote = (event: MouseEvent<HTMLButtonElement>) => {
        if (event) {
            event.preventDefault();

            setNotes([...notes, new Note(newNote)]); 
            setNewNote("");                       
        }
    }
    ...
}
...

Finally, we need to bind our onCreateNote function to the button onClick event as an event handler. This will create our note and add it to our notes list.

/* NoteListingComponent.tsx */
...
const NoteListingComponent: React.FC<NoteListingComponentProps> = (props) => {
   
    ...

    return <>
        ...
        <button type="submit" onClick={onCreateNote}>Submit</button>
        ...
    </>
}
...

That's it! We can now write a new note message and add it to our notes listing.

Creating a note in a React TypeScript Notes app
Creating a note in a React TypeScript Notes app

Calling a button onClick event handler in another component

Our next task is to call a button onClick event handler in another component.

We will demonstrate this by deleting a note from our notes listing.

The issue with this is that our delete button is located in NoteViewComponent.

However, we need to delete the note from our notes list. The notes list is located in NoteListingComponent, so that means we need to set the function in NoteListingComponent.

So, how do we go about setting an event handler to an event in another component?

Well, what we can do is set a new event handler in the NoteViewComponentProps interface. When we create a NoteViewComponent instance, it will need to pass in the event handler as a parameter.

/* NoteViewComponent.tsx */
import React, {MouseEventHandler} from "react";
...

interface NoteViewComponentProps {
    Note: Note;
    OnDeleteNote: MouseEventHandler<HTMLButtonElement>
}

...

Now that we have created an event handler for deleting a note, we need to create a delete button and bind our event handler to the delete button's onClick event.

/* NoteViewComponent.tsx */
...
const NoteViewComponent: React.FC<NoteViewComponentProps> = (props) => {

    return <li>
    <span>{props.Note.Message}</span>
    <span>Created: {props.Note.Created.toString()}</span>
    <button type="submit" onClick={props.OnDeleteNote}>Delete</button>
    </li>
}
...

Next, we can move to our NoteListingComponent, and set up the delete note function. What this will do is pass in the array index number of the note we wish to delete. The app will then filter out the note with that index number from the notes list.

/* NoteListingComponent.tsx */
...
const NoteListingComponent: React.FC<NoteListingComponentProps> = (props) => {
   
    ...

    const onDeleteNote = (event: MouseEvent<HTMLButtonElement>, index: number) => {
        if (event) {
            event.preventDefault();

            setNotes(notes.filter((note, noteIndex) => noteIndex != index));
        }
    }    

	...
}
...

Finally, as we set up the onDeleteNote function in NoteViewComponent, we now need to bind the event handler to it. As well as the event, we also pass in the array index as a parameter. This is so the app knows which note to delete.

/* NoteListingComponent.tsx */
...
const NoteListingComponent: React.FC<NoteListingComponentProps> = (props) => {
   
	...
            {notes && notes.length > 0 ? (
            <ul>
                {notes.map((note: Note, index: number) => {
                    return <NoteViewComponent key={`Note`+index} Note={note} OnDeleteNote={(event) => onDeleteNote(event, index)}></NoteViewComponent>
                })}
            </ul>    
            ) : (
                <p>You currently do not have any saved notes.</p>
            )}
	...
}
...

We can now successfully delete notes from our note app.

Deleting a note in a React TypeScript Notes app
Deleting a note in a React TypeScript Notes app

Using onMouseOver and onMouseOut to change the background colour

We will look at some of the other React events, and for this, we will use the onMouseOver and onMouseOut React events to change the background colour of a note when the mouse hovers over it.

In note-view-component.css, we have added a .highlight class which sets the background colour to grey.

/* note-view-component.css */
...
.highlight {
    background-color: #ccc;
}

Next, we are going to use the React useState hook to declare a class name that we will be able to change.

/* NoteViewComponent.tsx */
...

const NoteViewComponent: React.FC<NoteViewComponentProps> = (props) => {

    const [className, setClassName] = useState('');

    ...
}
...

Afterwards, we are going to set up two functions. The onChangeBackground function will set the class name to highlight, and the onRemoveBackground function will empty the class name.

/* NoteViewComponent.tsx */
import React, {useState, MouseEvent, MouseEventHandler} from "react";
...

const NoteViewComponent: React.FC<NoteViewComponentProps> = (props) => {

    ...

    const onChangeBackground = (event: MouseEvent<HTMLLIElement>) => {
        if (event) {
            setClassName("highlight");
        }
    };

    const onRemoveBackground = (event: MouseEvent<HTMLLIElement>) => {
        if (event) {
            setClassName("");
        }
    };

    ...
}
...

We now need to set up these functions as event handlers. As we are going to change the background colour for our note, we are going to set these as events in our <li> tag.

The onMouseOver will be the first event, and this is used when a mouse is hovering over the element. We will bind our onChangeBackground function to the onMouseOver event. This means that when we hover our mouse over a note, it will change the background colour to grey.

To remove the background, we will use the onMouseOut event, and this will bind to our onRemoveBackground function. This will remove the .highlight class from the <li> tag, which will restore the background colour to white.

/* NoteViewComponent.tsx */
...

const NoteViewComponent: React.FC<NoteViewComponentProps> = (props) => {

    ...

    return <li className={className} onMouseOver={onChangeBackground} onMouseOut={onRemoveBackground}>
    <span>{props.Note.Message}</span>
    <span>Created: {props.Note.Created.toString()}</span>
    <button type="submit" onClick={props.OnDeleteNote}>Delete</button>
    </li>
}
...

This now allows us to highlight a note by moving our mouse cursor over it.

Change the background colour in React TypeScript Notes app using onMouseOver and onMouseOut
Change the background colour in React TypeScript Notes app using onMouseOver and onMouseOut

Asynchronous event handlers

We will show you how to set up an asynchronous event handler. Using an async event handler is important, particularly for making API calls where the response times could be dependant on many factors, such as internet connection, or database performance.

Using an async event handler means that the app isn't locked down whilst a particular event handler is being executed.

To demonstrate how this works, we are going to implement a two-second delay when creating a note. The note will not appear on our notes listing for at least two seconds.

For this example, we are set the function into a while loop for two seconds. When this happens, we are unable to do anything else with the app. The background colour does not change when hovering over another note when we are caught in this loop.

/* NoteListingComponent.tsx */
...
const NoteListingComponent: React.FC<NoteListingComponentProps> = (props) => {
   
    ...

    const onCreateNote = (event: MouseEvent<HTMLButtonElement>) => {
        if (event) {
            event.preventDefault();

            var comingDate = new Date();

            comingDate.setSeconds(comingDate.getSeconds() + 2);

            while (comingDate > new Date()) {

            }
            
            setNotes([...notes, new Note(newNote)]); 
            setNewNote("");                       
        }
    }

    ...
}
...

To resolve this issue, we can set up a async function for this event. We can use the JavaScript setTimeout function and delay it for two seconds (or 2000 milliseconds).

In the async function, we can wrap the setTimeout into a Promise and await the Promise result.

/* NoteListingComponent.tsx */
...
const NoteListingComponent: React.FC<NoteListingComponentProps> = (props) => {
   
    ...

    const onCreateNoteAsync = async(event: MouseEvent<HTMLButtonElement>) => {
        if (event) {
            event.preventDefault();

            await new Promise(resolve => setTimeout(resolve, 2000));

            setNotes([...notes, new Note(newNote)]); 
            setNewNote("");                       
        }         
    }    

    ...
}
...

The final task is to bind our async function to the button onClick event as an async event handler. We need to declare the button onClick event as async, and use the await keyword.

/* NoteListingComponent.tsx */
...
const NoteListingComponent: React.FC<NoteListingComponentProps> = (props) => {
   
    ...

    return <>
        ...
        <button type="submit" onClick={async(e) => await onCreateNoteAsync(e)}>Submit</button>
        ...
    </>
}
...

When running our application now, we can always hover over a note, even if we in the process of the two second delay.

Watch our tutorial and try it out

Watch us demonstrate how to implement these React events, and how to set up an async event handler in our video below:

In addition, download our code example to try it out and give it a go.

Have any comments about this article?

If you have any questions or comments about this article, any business opportunities, or any feedback about the site in general, we would love to hear from you! Contact us

David Grace

David Grace

I am a .NET developer, building web applications in .NET Framework and .NET Core with a SQL Server database.

Some of the .NET packages I have used include Entity Framework and MVC.

I've also used many JavaScript frameworks such as React and jQuery, and have experience building CSS in SASS.

Twitter Feed