Gordon Maloney
Gordon Maloney

Reputation: 392

Struggling to correctly update and re-render chart on react-chartjs-2

I am building an app for gym-goers to record and track their progress. For each exercise they input, it should render a chart showing their previous track record. This is working fine.

Users can then add another entry to this track record, but it does not update the chart unless you refresh the page. I can't work out why or how to fix it.

There are a number of different components involved - a parent Exercise.js one, then an ExerciseFooter.js one, which contains the buttons to adjust the target or add a new entry to the exercise, and then AddHistory.js and SetTarget.js components which contain modals and the logic to update the exercise via Redux and MongoDB.

A minimal version of the Exercise.js page is here (I've collapsed the stuff that's mainly styling into single lines as much as possible):

import React, { useState, useEffect } from "react";
import { ExerciseFooter } from "./ExerciseFooter";
import { Line } from "react-chartjs-2";
import { useLocation } from "react-router-dom";
import { useSelector } from "react-redux";

export const Exercise = (props) => {
  const location = useLocation();
  const users = useSelector((state) => state.auth);
  const localUser = JSON.parse(localStorage.getItem("profile"));

  const [user, setUser] = useState("");

  const [exerciseProp, setExerciseProp] = useState({
    history: [""],
    target: 0,
  });

  useEffect(() => {
    localUser &&
      localUser?.result &&
      users.length > 0 &&
      setUser(
        users.filter(
          (filteredUser) => filteredUser._id == props.match.params.userId
        )[0]
      );

    if (!localUser) setUser("");

    setExerciseProp(
      user?.exercises?.filter(
        (exercise) => exercise._id == props.match.params.exerciseId
      )[0]
    );
  }, [users, location]);

  //styling for chart
  const [barData, setBarData] = useState({
    labels: [""],
    datasets: [
      { label: "Target", fill: false, radius: 0, data: [""], borderColor: ["rgba(35, 53, 89)"], borderWidth: [3], },
      { label: "You did", data: [""], tension: 0.3, borderColor: ["white"], backgroundColor: ["white"], borderWidth: 3, },
    ],
  });

  //updating chart data
  var weightArr = [];
  var dateArr = [];
  var targetArr = [];

  if (exerciseProp) {
    exerciseProp.history.map((hist) =>
      weightArr.push(parseInt(hist.weight) || 0)
    );
    exerciseProp.history.map((hist) => dateArr.push(hist.date));

    for (let i = 0; i < exerciseProp.history.length; i++) {
      targetArr.push(exerciseProp.target);
    }
  }

  useEffect(() => {
    if (exerciseProp) {
      setBarData({
        labels: dateArr,
        datasets: [
          {
            label: "Target",
            fill: false,
            radius: 0,
            data: targetArr,
            borderColor: ["rgba(35, 53, 89)"], borderWidth: [3],
          },
          {
            label: "Weight",
            data: weightArr,
            tension: 0.3, borderColor: ["white"], backgroundColor: ["white"], borderWidth: 3,
          },
        ],
      });
    }
  }, [users]);


  //render chart ones exerciseProp is populated
  if (exerciseProp) {
    return (
      <div style={{ marginTop: "200px" }}>
        <Line
          data={barData}
          options={{ plugins: { title: { display: false, }, legend: { display: false, }, },
          scales: { x: { grid: { color: "white", font: { family: "Dongle", size: 20, }, }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, y: { grid: { color: "white", }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, }, }}
        />

        {exerciseProp && <ExerciseFooter user={user} exercise={exerciseProp} />}
      </div>
    );
  } else {
    return <>Loading...</>;
  }
};

I've tried doing a few different things but nothing has worked. I tried adding an 'update' state variable which was updated by a function passed down to the the various dispatches, and then added it to the dependencies of the useEffects, but that didn't seem to make any difference.

Any help much appreciated! As I say, if I just force a refresh then it works fine but know that's bad practice so trying to work out why it isn't re-rendering correctly.

Thanks!

Upvotes: 2

Views: 2802

Answers (1)

Ammar yasser
Ammar yasser

Reputation: 300

You just have to enable redraw prop

like this

<Line
redraw={true}
data={barData}
options={{ plugins: { title: { display: false, }, legend: { display: false, }, },
    scales: { x: { grid: { color: "white", font: { family: "Dongle", size: 20, }, }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, y: { grid: { color: "white", }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, }, }}/>

this all you have to do

redraw={true}

Upvotes: 4

Related Questions