Implement onError to Handle Image Lading Errors
Implement onError to handle image loading errors in the <Avatar /> component.
First, open Avatar.test.tsx and update your imports to include both cleanup and fireEvent and beforeEach like so:
import { describe, expect, it, beforeEach } from "vitest";
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
Then inside the describe use the beforeEach like this:
describe("Avatar", () => {
beforeEach(() => {
cleanup()
})
// code omitted...
})
Now we're ready. Before we implement onError in our <Avatar /> component, add these tests:
it("should use a fallback image if image fails to load", () => {
render(<Avatar url="https://hello.com/fake.png" />);
const img = screen.getByAltText(FALLBACK_AVATAR_ALT_TEXT);
fireEvent.error(img)
expect(img).toHaveAttribute("src", FALLBACK_AVATAR_URL);
});
it("should use a fallback image if url is empty string", () => {
render(<Avatar url="" />);
const img = screen.getByAltText(FALLBACK_AVATAR_ALT_TEXT);
fireEvent.error(img)
expect(img).toHaveAttribute("src", FALLBACK_AVATAR_URL);
});
Both of these ensure image loading errors are handled properly, which we’ll do so using our onError handler. Open the terminal and run yarn test to see them fail.
Now open Avatar.tsx and let’s fix these.
First, set up a state value called srcToRender and setSrcToRender with the initial value as src.
Now modify the onError function to call setSrcToRender and pass in FALLBACK_AVATAR_URL
Open the terminal again, and you’ll see that our tests are now passing! Voila!
Resources
The following resources provide more information for this lesson:
- State: A Component’s Memory
- React Testing Library: cleanup
- React Testing Library: Firing Events
- StackOverflow: Testing img.onLoad/img.onError using Jest and React Testing Library
Transcript
The first note we need to do before anything is import a few things. This is something that I should have done in the beginning, but didn't need to until this lesson.
We are going to call beforeEach inside of our describe block, and in that function, we're going to call cleanup. What that does is, before each of these tests, it cleans up the DOM so that we have a fresh DOM between each test.
Now we're going to come down here and we're going to add new test. We're going to add these two new tests, and the first one is going to check that we use a fallback image if the image fails to load, so we'll pass in some fake image URL. We'll get our image like before, and then what we're going to do is fire the error event, which should trigger our onError handler.
After that is called, we will have updated the source attribute of the image to the fallback avatar URL. The second test is very similar except that for URL, we're just passing an empty string. Let's copy this, scroll to the top, and we're going to import that from testing library.
Now what we're going to do is open up the terminal and run yarn test. We should see both of our tests fail. You have two failures, so we'll close this, and now let's fix it.
Open up Avatar.tsx. It comes at the very top, and we're going to import useState from react. Then we'll come back down, and now we're going to actually use that. We're going to call it srcToRender and setSrcToRender. That's going to be equal to useState, and we're going to pass in the initial URL.
Then we're going to come over here on line 20 and here, we're going to change that to srcToRender, and now inside of our onError function, we are going to call setSrcToRender, and we're going to pass in the fallback URL. We can delete our notes.
Essentially, what we're doing now is we're keeping track of our URL and state, and we're passing that value to the source attribute on image. Whenever the onError handler is called, which is when there's an error event for the image, it's going to call setSrcToRender and render our fallback avatar URL, or rather set that fallback avatar URL as the source.
Call this, we update state with this value. That's going to trigger re-render, and it's going to render it here. Now if we open up the terminal again, right, [laughs] there we go. We'll see now all of our tests are passing. Awesome.