React.js: Using the "useTransition()" hook to draw the self-updated chart

The React.js library is improvising with its power and provides several features to design feature rich composable applications. The hooks are the most important features of this library. These hooks are used for state updates, side-effect handling, context data sharing, etc. In React 18 the useTransition() hook is introduced. 

The useTransition() Hook

The useTransition() hook is a React hook that helps to handle state updates in a non-blocking manner. This is particularly useful when dealing with asynchronous operations like API calls or expensive computations. This hook helps to prevent the UI freezes. We use this hook to update the state without bocking the UI.     

The useTransition hook does not take any parameters. The useTransition returns an array with following two items:

  • The isPending flag that specifies whether there is a pending Transition
  • The startTransition function, this help to mark a state update as a Transition. The startTransition does not return anything. 
The useTransition hook must be called at the top level of the component to make state updates as a non-blocking Transitions. These transitions help to keep the user interface updates responsive even the devices are slow. The transition keeps the UI responsive in the middle of re-rendering the component. 

In this article, we will use the useTransition to draw the chart by accessing the API to fetch the data to plot the chart. To draw the chart, we will be using the Chart.js and react-chart-2 packages. The Chart.js, is a simple and flexible JavaScript charting library for the modern web application to draw charts. The react-chart-2 is a React Component for Chart.js.       

To fetch the data, I have created an ASP.NET Core API that returns the WeatherForecast details like Temperature and Summary. (The default API Code of ASP.NET Core 8 API). The code for this API is shown in Listing 1.


using Microsoft.AspNetCore.Http.Json;

var builder = WebApplication.CreateBuilder(args);


// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.Configure<JsonOptions>(options =>
{
    options.SerializerOptions.PropertyNamingPolicy = null;
});


builder.Services.AddCors(options => 
{
   options.AddPolicy("cors", policy =>
   {
       policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
   });    
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}


app.UseHttpsRedirection();
app.UseCors("cors");

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/wf", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();

app.Run();

internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

Listing 1: The API Code

Run the API. I am running it from the port 5000.
  
Create a React.js application using the language TypeScript. Name this application as my-react-chart-app using the following command.

npx create-react-app my-react-chart-app --template typescript

In this project add axios, bootstrap, chart.js and react-chartjs-2 packages using the following command

npm install bootstrap chart.js react-chartjs axios

In this project add a new component and name it as WeatherChartComponent. In this component add the code for accessing the API to fetch the Weather data for drawing the chart. We will be using the useEffect() hook to access the API and then the startTransition() method will be used to draw the chart based on the data received from the API. We will use the useState() hook to define an initial data for the chart. This data will be updated based on the data received from the API using the useEffect() hooks and then using the useTransition() hook we will keep drawing the chart and keep the UI responsive. The Listing 2 shows the code for the component.


import React, { useEffect, useState, useTransition } from 'react';
  import { Line, Bar } from 'react-chartjs-2';
import axios from 'axios';

/* Import elements */
import { Chart, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, PointElement,LineElement,LineController  } from 'chart.js';
Chart.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, PointElement,LineElement,LineController );

const WeatherChartComponent = () => {
  /* Define the initial data from the chart */
  const [chartData, setData] = useState({
    labels: [],
    datasets: [
      {
        label: 'The Weather Forecast Chart',
        data: [], /* The Data that will be used to draw the chart */
        borderColor: 'blue',
        borderWidth: 2,
        fill: false,
      },
    ],
  });

  const [isPending, startTransition] = useTransition();

  useEffect(() => {
    const fetchChartData = async () => {
      try {
        const response = await axios.get('http://localhost:5000/wf');
        const data = response.data;

        startTransition(() => {
          setData((prevData) => ({
            ...prevData,
            labels: data.map((item:any) => item.Summary),
            datasets: [
              {
                ...prevData.datasets[0],
                data: data.map((item:any) => item.TemperatureF),
              },
            ],
          }));
        });
      } catch (error) {
        console.error('Error fetching data', error);
      }
    };

    fetchChartData();

    const interval = setInterval(fetchChartData, 3000); // Fetch data every 3 seconds

    return () => {
        clearInterval(interval);
        /* Dispose the Chart */
        Object.keys(Chart.instances).forEach((key) => {
          const instance = Chart.instances[key];
          if (instance) {
            instance.destroy();
          }
        });
      }
  }, [startTransition]);

  return (
    <div className="container">
      <h2>Dynamic Chart</h2>
      {isPending && <p>Loading...</p>}
      <Line data={chartData}   />
      <br/> 
        {/* <Bar data={chartData} /> */}
    </div>
  );
};

export default WeatherChartComponent;
Listing 2: The WeatherChartComponent code
     
As shown in Listing 2, the Chart.register() method is used to register the chart components like plugins, scales and chart types globally so that Chart will be available globally. The call to the API is made after each 3 seconds so fetch the chart data so that it can be drawn. One more important thing is that we must dispose the chart instance.

Modify the index.tsx to render the WeatherChartComponent. Run the application using the following command

npm run start


The Chart will be shown in the browser as shown in Figure 1



Figure 1: The result
 
You can see that the Chart will keep refreshing after each 3 seconds. The UI will not be clocked while the state is being updated. The Following video shows the updating chart.




Conclusion: The useTransition() is the most useful hook in React.js that helps to design the responsive UI  

Popular posts from this blog

Uploading Excel File to ASP.NET Core 6 application to save data from Excel to SQL Server Database

ASP.NET Core 6: Downloading Files from the Server

ASP.NET Core 6: Using Entity Framework Core with Oracle Database with Code-First Approach