Creating and testing a Simple Weather App in ReactJS: A Step-by-Step Guide
In this article, we will show you how to create and test a simple weather application in ReactJS using Vite and TypeScript instead of create-react-app. The weather application will consist of a form where the user can enter the name of a city and a button to submit the form. When the form is submitted, the application will make a request to the OpenWeatherMap API to retrieve the current weather data for the specified city. The weather data will be displayed on the page, including the city name, temperature, and description of the weather conditions.
To use the OpenWeatherMap API, we will need an API key. You can create a free account on the OpenWeatherMap website to get your own API key, or you can sign up for a paid plan to access additional features and higher usage limits. The API documentation can be found at http://api.openweathermap.org/.
To improve the performance of the application, we will use the useMemo
and useCallback
hooks to memoize expensive calculations and avoid unnecessary re-renders of components. We will also use the useRef
hook to create a reference to the city input element and avoid re-creating it on every render.
In addition, we will use the lodash
library to debounce the form submission
Let’s get started! 👌
First, we will create a new project using Vite
, which is a lightweight and fast alternative to create-react-app
. To create a new project, open your terminal and run the following commands:
npm create vite@latest weather-app -- --template react-ts
Next, we will install the axios
library, which we will use to make HTTP requests to the OpenWeatherMap API. We will also install the lodash.debounce
function, which we will use to debounce
the API requests to avoid making too many requests in a short amount of time.
npm install axios lodash.debounce
Now that we have our project set up, we can start building our weather application. We will start by creating a simple form that allows users to enter the name of the city they want to get the weather data for.
In the src
directory of your project, create a new file called WeatherForm.tsx
and add the following code:
This component is a simple form that allows users to enter the name of the city they want to get the weather data for. We have used the useCallback
hook to create memoized versions of the onChange
and onSubmitForm
functions. This means that these functions will only be re-created if one of the dependencies in the second argument to useCallback
changes.
By using the useCallback
hook, we can avoid unnecessary re-creations of the onChange
and onSubmitForm
functions, which can improve the performance of the WeatherForm
component and the overall application.
onChange
and onSubmitForm
functions in the WeatherForm
component will only be re-created if their dependencies change, which can improve the performance of the component and the overall application.
Next, we will create a component that displays the weather data for a given city. Create a new file called WeatherData.tsx
and add the following code:
This component takes the city, temperature, and description of the weather as props and displays them in a simple format.
Now that we have our form and data display components, we can create our main weather component that brings everything together. Create a new file called Weather.tsx
and add the following code:
In Weather.tsx
component, we have used the useCallback
hook to create a memoized version of the fetchWeatherData
function. This means that the function will only be re-created if one of the dependencies in the second argument to useCallback
changes.
We have also used the useMemo
hook to create a memoized version of the debounce
function that is applied to the fetchWeatherData
function. This means that the debounce
function will only be re-created if the fetchWeatherData
function changes, which will happen if one of its dependencies changes.
By using these hooks, we can avoid unnecessary re-creations of the fetchWeatherData
and debounce
functions, which can improve the performance of the application.
fetchWeatherData
and debounce
functions will only be re-created if their dependencies change, which can improve the performance of the application.
You will need to add the following lines to your .env
file:
API_KEY=your_api_key
Be sure to replace your_api_key
with the actual value for your API key.
Note that the .env
file should not be committed to version control. It should be used only for local development, and the values should be different in production.
Testing
To write unit tests for the weather application, we can use a testing framework like jest
and @testing-library/react
. We can start by writing a test for the WeatherForm
component to ensure that it correctly handles user input and calls the onSubmit
callback when the form is submitted.
First, we need to add the jest
and @testing-library/react
dependencies to our project. We can do this by running the following command:
npm install --save-dev jest @testing-library/react
Next, we can create a __tests__
directory in the root of our project and add a WeatherForm.test.tsx
file inside it. This file will contain our test for the WeatherForm
component.
In this test, we are using the render
function from @testing-library/react
to render the WeatherForm
component. We are then using the getByLabelText
and getByText
functions to get a reference to the city input and the submit button in the form.
We are using the fireEvent
function to simulate user interactions with the form by changing the value of the city input and clicking the submit button.
Finally, we are using expect
and the toHaveBeenCalledWith
matcher from jest
to verify that the onSubmit
callback was called with the correct city name when the form was submitted.
After writing this test, we can run it by using the jest
command:
jest
If the test passes, we should see a message like this:
PASS __tests__/WeatherForm.test.tsx
WeatherForm
✓ should call the onSubmit callback when the form is submitted (5ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.849s
Ran all test suites.
We can write a test for the WeatherData
component to ensure that it correctly displays the city, temperature, and description data passed to it as props.
Add the following test to the WeatherForm.test.tsx file:
In this test, we are using the render
function from @testing-library/react
to render the WeatherData
component with some sample data. We are then using the getByText
function to get a reference to the city, temperature, and description elements in the component and using the toBeInTheDocument
matcher from jest
to verify that they are correctly displayed.
After writing this test, we can run it by using the jest
command:
jest
If the test passes, we should see a message like this:
PASS __tests__/WeatherForm.test.tsx
WeatherForm
✓ should call the onSubmit callback when the form is submitted (5ms)
✓ should display the city, temperature, and description data (5ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.371s
Ran all test suites.
We can continue to write additional tests for the weather application to ensure that all of its components are working correctly. By writing unit tests, we can catch bugs and errors early in the development process and ensure that our application is working as expected.
Refactoring
To further improve the performance of the WeatherForm.tsx
component, we can use the useRef
hook to create a reference to the city input element. This will avoid re-creating the input element every time the component is rendered, which can improve the performance of the component and the overall application.
Update the WeatherForm.tsx
file with the following code:
In this updated version of the WeatherForm.tsx
component, we are using the useRef
hook to create a reference to the city input element. We are then using this reference in the onSubmitForm
function to access the value of the city input without re-creating the input element every time the component is rendered.
By using the useRef
hook, we can avoid unnecessary re-creations of the city input element, which can improve the performance of the WeatherForm
component and the overall application.
After making these changes, our weather application should continue to work as before, but the WeatherForm
component will be more efficient and will use the useRef
hook to create a reference to the city input element.
⚠ ⚠ ⚠ Using the useMemo
and useCallback
hooks on every component and function can increase the complexity of the code and make it more difficult to understand and maintain. It is important to use these hooks wisely and only on those components, functions, and values that truly benefit from memoization or callback caching.
Overuse of the useMemo
and useCallback
hooks can lead to unnecessary complexity and can even negatively impact the performance of the application if the memoized values or callbacks are not used frequently enough.
In general, it is best to use the useMemo
and useCallback
hooks only when there is a clear benefit to memoizing a value or caching a callback, such as avoiding expensive calculations or re-creations of complex objects or avoiding unnecessary re-renders of components. Otherwise, it is better to avoid using these hooks and let the React framework manage the component rendering and optimization process.
Thank you for reading my article. I hope you found it informative and helpful. If you have any questions or feedback, please feel free to contact me. I would also be grateful if you would share this article with others who may be interested. Thank you again for your time and attention.