Next.js external script not loading properly, second client component won’t display

I am trying to learn React by creating a small and simple web app with Next.js, but I am seeing some very strange bugs.

The app uses a node package to display charts from data that comes from a database. It sets a javascript interval to refresh its data. I could write this app without React in 20 minutes, but trying to use React to develop it has taken a week and it still isn’t working. I’ll explain the problems below.

Here are my 3 source files. (To save space, I removed the SQL queries. They are well-tested and are not causing these problems.)

page.tsx

import Chart from './Chart.tsx';

const ServerComponent = () => {
    return (
        <div>
            <Chart divID="xau-usd-candlestick" refreshMinutes="5" chartType="candlestick" currencyFrom="xau" currencyTo="usd" decimalPlaces="2" />
            <Chart divID="cny-usd-candlestick" refreshMinutes="5" chartType="candlestick" currencyFrom="cny" currencyTo="usd" decimalPlaces="4" />
            <Chart divID="try-usd-candlestick" refreshMinutes="5" chartType="candlestick" currencyFrom="try" currencyTo="usd" decimalPlaces="6" />
            <Chart divID="irr-usd-candlestick" refreshMinutes="5" chartType="candlestick" currencyFrom="irr" currencyTo="usd" decimalPlaces="8" />
            <Chart divID="btc-usd-candlestick" refreshMinutes="5" chartType="candlestick" currencyFrom="btc" currencyTo="usd" decimalPlaces="2" />
        </div>
    );
};

export default ServerComponent;

Chart.tsx

'use client'

import styles from './page.module.css';
import { useState, useEffect } from 'react';
import Script from 'next/script'

async function renderChart (divID, chartType, currencyFrom, currencyTo, decimalPlaces)
{
    let url = 'http://localhost:3000/api?chart-type=' + chartType;
    url += '&cur-from=' + currencyFrom + '&cur-to=' + currencyTo + '&decimal-places=' + decimalPlaces;

    await fetch (url)
        .then ((response) => { return response.text (); })
        .then ((data) => { Highcharts.stockChart (divID, JSON.parse (data).data); })
        .catch ((error) => { console.log (error, currencyFrom); });
}

function Chart (props)
{
    if (typeof props.refreshMinutes != 'undefined' && props.refreshMinutes > 0)
    {
        useEffect (() => {
            const interval = setInterval (renderChart, props.refreshMinutes * 60000, props.divID, props.chartType, props.currencyFrom, props.currencyTo, props.decimalPlaces);
            return () => { clearInterval (interval); };
        }, []);
    }

    let css = `${styles.chart}`;

    return (
        <div className={styles.main}>
            <div id={props.divID} className={css}>
            </div>
            <Script
                src = "https://code.highcharts.com/stock/highstock.js"
                onReady = { () => { renderChart (props.divID, props.chartType, props.currencyFrom, props.currencyTo, props.decimalPlaces); }}
            />
        </div>
    );
}

export default Chart;

api/route.ts

const { Client } = require ('pg');

const pg_client =
{
  host: '127.0.0.1',
  port: 5432,
  database: 'db_name',
  user: 'xxxxx',
  password: 'xxxxx'
};

const db_client = new Client (pg_client);
async function connect () {
    await db_client.connect ();
}
await connect ();

async function getCandlestickData (cur_from, cur_to, decimal_places)
{
    return new Promise (async (resolve, reject) =>
    {
        var query = 'select .....';
        await db_client.query (query)
            .then ((db_result) =>
            {
                for (let i = 0; i < db_result.rows.length; i++)
                {
                    db_result.rows [i].x = Number (db_result.rows [i].x) * 1000;
                    db_result.rows [i].open = Number (Number (db_result.rows [i].open).toFixed (decimal_places));
                    db_result.rows [i].close = Number (Number (db_result.rows [i].close).toFixed (decimal_places));
                    db_result.rows [i].low = Number (Number (db_result.rows [i].low).toFixed (decimal_places));
                    db_result.rows [i].high = Number (Number (db_result.rows [i].high).toFixed (decimal_places));
                }

                let result =
                {
                    plotOptions: {
                        candlestick: {
                            color: 'pink',
                            lineColor: 'red',
                            upColor: 'lightgreen',
                            upLineColor: 'green'
                        }
                    },
                    rangeSelector: { selected: 1 },
                    title: { text: cur_from + '-' + cur_to },
                    series: [
                                {
                                    type: 'candlestick',
                                    name: cur_from + '-' + cur_to,
                                    data: db_result.rows
                                }
                            ]
                };

                resolve (result);
            })
            .catch ((error) => { reject (error); });
    });
}


export async function GET (request: Request) {

    let data;
    let url = new URL (request.url);
    let chartType = url.searchParams.get ('chart-type');

    if (chartType == 'candlestick')
    {
        let currencyFrom = url.searchParams.get ('cur-from').toUpperCase ();
        let currencyTo = url.searchParams.get ('cur-to').toUpperCase ();
        let decimalPlaces = url.searchParams.get ('decimal-places').toUpperCase ();
        await getCandlestickData (currencyFrom, currencyTo, decimalPlaces).then ((result) => { data = result; });
    }
    return Response.json({ data });
}

There are several problems I’m seeing, but here are the main two:

The weirdest thing I am noticing is that the second chart in the list does not display. If I rearrange the <Chart> elements in page.tsx, it is still the second one that doesn’t display. It seems like the onReady function for the <Script> element never gets called but I have no idea why. It works fine for the other elements, but never for the second one.

At random times, I get the error Highcharts is not defined. So the code is trying to use the Highcharts object before it is fully loaded? I thought the onReady event was supposed to prevent that. Is there something wrong with the way I am using the <Script> tag? Is there a better way to load an external script than the way I am doing it?