Problem getting c3 chart to load in React component

26 views Asked by At

I tried to follow both the c3 charts docs here: https://c3js.org/samples/chart_donut.html

And this stack overflow here: C3 chart not rendering in react project

But it is still not rendering nor are my console logs showing which means its not being initiating. What logic am I missing?

// rrd imports
import { useLoaderData, useOutletContext } from "react-router-dom";
import { useState, useEffect } from "react";

// library
import { toast } from "react-toastify";
import c3 from 'c3';
import 'c3/c3.css'; // Import the CSS file for styling


import LoadingSpin from '../assets/loading-spin.svg' // Adjust the path to match your file structure

// components
import AddExpenseForm from "../components/AddExpenseForm";
import BudgetItem from "../components/BudgetItem";
import Table from "../components/Table";

// helpers
import { createExpense, deleteItem } from "../helpers";

// loader
export async function budgetLoader({ params }) {



  const response = await fetch(`/api/budgets/${params.id}`);
  const budget = await response.json();



  if (!budget) {
    throw new Error("The budget you’re trying to find doesn’t exist");
  }

  return { budget };
}

// action
export async function budgetAction({ request }) {
  const data = await request.formData();
  const { _action, ...values } = Object.fromEntries(data);

  if (_action === "createExpense") {
    try {
      await createExpense({
        name: values.newExpense,
        amount: values.newExpenseAmount,
        budgetId: values.newExpenseBudget,
        category: values.newExpenseCategory,
        currency: values.newExpenseCurrency
      });

      console.log(values, "what values are here in budget page")

      return toast.success(`Added ${values.newExpense} to budget!`);
    } catch (e) {
      throw new Error("There was a problem creating your expense.");
    }
  }

  if (_action === "deleteExpense") {
    try {
      deleteItem({
        key: "expenses",
        id: values.expenseId,
      });
      return toast.success("Expense deleted!");
    } catch (e) {
      throw new Error("There was a problem deleting your expense.");
    }
  }
}

const BudgetPage = () => {
  const { budget } = useLoaderData();
  const [loading, setLoading] = useState(true);
  const [dots, setDots] = useState('.');
  const [chart, setChart] = useState(null);

  useEffect(() => {
    const initializeChart = () => {
      console.log("Initializing chart...");

      // Extract data from expenses for the initial chart
      const chartData = budget.expenses.map((expense) => [
        expense.name,
        expense.amount,
      ]);

      console.log("Chart data:", chartData);

      const newChart = c3.generate({
        bindto: '#chart', // Specify the element where the chart will be rendered
        data: {
          columns: chartData,
          type: 'donut',
          onclick: function (d, i) {
            console.log('onclick', d, i);
          },
          onmouseover: function (d, i) {
            console.log('onmouseover', d, i);
          },
          onmouseout: function (d, i) {
            console.log('onmouseout', d, i);
          },
        },
        donut: {
          title: 'Expenses Distribution',
        },
      });

      console.log("Chart initialized:", newChart);

      // Unload some data after another delay (2500 milliseconds in this example)
      setTimeout(() => {
        const idsToUnload = budget.expenses.slice(0, 2).map((expense) => expense.id);
        newChart.unload({
          ids: idsToUnload,
        });
        console.log("Data unloaded from chart:", idsToUnload);
      }, 2500);

      setChart(newChart); // Set the chart instance in the state
    };

    // Invoke the initializeChart function
    initializeChart();
  }, [budget.expenses]);
  

  useEffect(() => {
    const timer = setTimeout(() => {
      setLoading(false);
    }, 3000);

    return () => clearTimeout(timer);
  }, []);

  useEffect(() => {
    if (loading) {
      const dotsTimer = setInterval(() => {
        setDots((prevDots) => (prevDots.length >= 3 ? '.' : prevDots + '.'));
      }, 500);

      return () => clearInterval(dotsTimer);
    }
  }, [loading]);

  if (loading || !budget) {
    return (
      <div
        className="grid-lg"
        style={{
          color: 'hsl(var(--accent))',
          fontWeight: 'bold',
          fontSize: 'clamp(1.94rem, calc(1.56rem + 1.92vw), 2.93rem)',
          display: 'flex',
          alignItems: 'center',
        }}
      >
        <img src={LoadingSpin} alt="Loading" width="40px" />
        Loading{dots}
      </div>
    );
  }

  return (
    <div
      className="grid-lg"
      style={{
        "--accent": budget.color,
      }}
    >
      <h1 className="h2">
        <span className="accent">{budget.name}</span> Overview
      </h1>
      <div className="flex-lg">
        <BudgetItem budget={budget} showDelete={true}/>
        <AddExpenseForm budgets={[budget]} />
      </div>
      {budget.expenses && budget.expenses.length > 0 ? (
      <div className="grid-md">
        <h2>
          <span className="accent">{budget.name}</span> Expenses
        </h2>
        {chart && <div id="chart">{chart}</div>}
        <Table expenses={budget.expenses} showBudget={false} />
      </div>
      ) : (
        <div
          className="grid-lg"
          style={{
            color: "hsl(var(--accent))",
            fontWeight: "bold",
            fontSize: "clamp(1.94rem, calc(1.56rem + 1.92vw), 2.93rem)"
          }}
        >
          <p>{budget.name} has no expenses</p>
        </div>
      )}
    </div>
  );
};

export default BudgetPage;
0

There are 0 answers