Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

frontend: convert chart.tsx component #2728

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

NicolaLS
Copy link
Contributor

@NicolaLS NicolaLS commented May 27, 2024

Convert the Chart class component in chart.tsx to a functional component.

There some general improvements that could be made, but this PR aims to perform minimal changes to only transform the component from a class component to a functional one. I will open another PR with a general refactor when this one was merged.

note: I moved the renderDate function out of the component, but this improvement should've been in the next PR not this one, lmk if I should put it back.

@NicolaLS NicolaLS force-pushed the refactor-functional-chart-III branch 2 times, most recently from ec9760f to d7e667c Compare May 28, 2024 16:55
@NicolaLS
Copy link
Contributor Author

addressed everything, PTAL @thisconnect :)

frontends/web/src/routes/account/summary/chart.tsx Outdated Show resolved Hide resolved

private onResize = () => {
const isMobile = window.innerWidth <= 640;
this.setState({ isMobile });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why isn't isMobile updated anymore on resize?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because it is calculated every time the component renders now. see here.

edit: ah, the component does not re-render automatically when the window re-sizes..good catch. I will convert isMobile to state again, and initialize it just like right now, but also setIsMobile in onResize then.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you past full links instead of "here", in some other comment you have like 3 links "here" "here" "this" this is hard for me to read.

also this should not update on ever render but on resize.

frontends/web/src/routes/account/summary/chart.tsx Outdated Show resolved Hide resolved
frontends/web/src/routes/account/summary/chart.tsx Outdated Show resolved Hide resolved
this.lineSeries.setData(data);
this.chart?.timeScale().fitContent();
this.setFormattedData(data);
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this effect is not needed, as the previous effect already and unconditionally calls initChart().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I also thought that, but I thought I should put it in there since there was this.

So yes logically it does not need to be there but if we want to strictly say we only want to convert the component it should be there? But I'll remove it and test if everything still works.

chart.current.unsubscribeCrosshairMove(handleCrosshair);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we explicitly want to run the effect once which is possible by passing an empty dependency array according to the react docs. however eslint wants us to either add the used dependencies, or remove the dependency array (which would cause it to run every render).

so we either ignore the warning with the comment or remove the dependency array but check if it already ran once e.g

const chartInitialized = useRef(false);

useEffect..
  if(!chartInitialized) ...chartInitialized = true, initChart() return cleanup
  else do nothing

so the function in use effect would return () => void | undefined not sure if thats even possible ? can it have no cleanup function when some condition is not met?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tldr; yes otherwise we get eslint errors

@NicolaLS NicolaLS force-pushed the refactor-functional-chart-III branch from d7e667c to 861e906 Compare May 29, 2024 14:49
@thisconnect
Copy link
Collaborator

componentDidMount and componentWillUnmount are replaced by this.

I believe "this" and a few other links broke. also it's hard to follow please consider using less links or paste full links. but if you rebase / squash etc. such links might break.

@NicolaLS
Copy link
Contributor Author

componentDidMount and componentWillUnmount are replaced by this.

I believe "this" and a few other links broke. also it's hard to follow please consider using less links or paste full links. but if you rebase / squash etc. such links might break.

ah I see..I think I will just remove the note now, thanks for letting me know. it was not super important just meant as a quick overview before starting the review but you already did that. (the links just pointed to the respective useEffects..)

@NicolaLS NicolaLS force-pushed the refactor-functional-chart-III branch from 861e906 to b95ae02 Compare May 29, 2024 15:43
@NicolaLS NicolaLS requested a review from thisconnect May 29, 2024 15:43
@thisconnect
Copy link
Collaborator

thisconnect commented May 30, 2024

tested b95ae02 webdev/servewallet and initChart is called, but somehow the chart isn't loading.

Screenshot 2024-05-30 at 11 13 54

@NicolaLS
Copy link
Contributor Author

tested b95ae02 webdev/servewallet and initChart is called, but somehow the chart isn't loading.
Screenshot 2024-05-30 at 11 13 54

actually, I forgot testing after removing the useEffect that calls initChart if !chart.current..but I tested right now without changing anything and webdev/servewallet seem to work fine for me on firefox and chrome..I'll look into this though.

@NicolaLS NicolaLS force-pushed the refactor-functional-chart-III branch from b95ae02 to 6f3304b Compare May 30, 2024 13:15
@NicolaLS
Copy link
Contributor Author

(rebase master force push )

@NicolaLS
Copy link
Contributor Author

tested b95ae02 webdev/servewallet and initChart is called, but somehow the chart isn't loading.
Screenshot 2024-05-30 at 11 13 54

@thisconnect so it is definitely the useEffect I removed, I added it back in and the chart shows again. I would argue that for a transformation of the code it is also correct that it is in there, since the original code also contains:

public componentDidUpdate(prev: Props) {
    const { chartDataDaily, chartDataHourly } = this.props.data;
    if (!this.chart) {
      this.createChart();
    }

which translates to the useEffect that runs every render. I agree that it should not be like this, but would it make sense to refactor this in another PR? Of course I can do it in this PR too but I think it would be better in another PR.

Do you have any env variables for react? Maybe the chart shows for me because react runs useEffect twice in dev mode (so you're not in dev mode?)

@NicolaLS NicolaLS force-pushed the refactor-functional-chart-III branch 3 times, most recently from f065771 to 01f035e Compare June 4, 2024 19:08
Copy link
Collaborator

@thisconnect thisconnect left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

found a weird issue, something seems wrong:

  • click week
  • rotate to BTC
  • white screen & js error
Screenshot 2024-06-07 at 09 19 21

@@ -14,8 +14,8 @@
* limitations under the License.
*/

import { LineData } from 'lightweight-charts';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import { LineData } from 'lightweight-charts';
import type { LineData } from 'lightweight-charts';

},
hideAmounts: false,

function getUTCRange() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use arrow functions if possible, I recently changed other functions see #2731

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know, so I'll always use them now. I was really confused on how to decide which ones to use..I kept the normal functions so the git diff is better because they don't need to be moved around (hoisting)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually typescript did not warn me when I changed this, had to move the functions around quite a bit so the order is correct and I did not get any runtime errors...

};
}

function renderDate(date: number, lang: string, src: string) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use newlines if there is more than 1-2 simple props.

toolTipTime,
} = tooltipData;


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: for readability and consistency there is no need to have multiple newlines, here and before

import { useContext, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { usePrevious } from '../../../hooks/previous';
import { ISummary, ChartData } from '../../../api/account';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import { ISummary, ChartData } from '../../../api/account';
import type { ISummary, ChartData } from '../../../api/account';

import { Component, createRef, ReactChild } from 'react';
import { ISummary } from '../../../api/account';
import { translate, TranslateProps } from '../../../decorators/translate';
import { useContext, useEffect, useRef, useState } from 'react';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: would be nice if react import is always first.

}
initChart();
return () => {
window.removeEventListener('resize', onResize);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initChart conditionally adds the event listener, but here it always removes the event.

I think this should be changed somehow. I guess the event can always be added?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes..I wanted to refactor this anyway in another PR but I guess it is a good idea to already do it here. I will add a second commit just for easier review which we can squash before merge.

@NicolaLS NicolaLS force-pushed the refactor-functional-chart-III branch from 01f035e to fd63af3 Compare June 7, 2024 14:51
@NicolaLS
Copy link
Contributor Author

NicolaLS commented Jun 7, 2024

found a weird issue, something seems wrong:

* click week

* rotate to BTC

* white screen & js error
Screenshot 2024-06-07 at 09 19 21

ah yes, this also happens when clicking on some account now and then going back to the account overview..its weird because I'm pretty sure that this was not an issue and some point, I don't know what change caused this (maybe it was an issue, I'm not hundred percent sure)

  • the problem is we set the state of chartDisplay and source when the switch in initChart matches anything (this is why there is no issue if 'all' is selected. this causes the chart.current to stay uninitialized causing the infinite loop.
    I will just fix the chart initialization now, thought it would be better in another PR but it seems like I can only get this one to work with a proper chart initialization logic.

  • I changed everything in the first commit but it stays broken with the issue you described here

  • I will add another commit fixing the initialization, but only for a easy review, the commits need to be squashed since the first one is broken

@NicolaLS NicolaLS force-pushed the refactor-functional-chart-III branch 2 times, most recently from 4a2af4b to d437512 Compare June 7, 2024 16:33
Copy link
Contributor Author

@NicolaLS NicolaLS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thisconnect I addressed all changes in the first commit and added a second one that refactors the chart initialization. When everything is fine I can squash them before merge.
PTAL :)

Comment on lines +215 to +216
chart.current?.timeScale().unsubscribeVisibleLogicalRangeChange(calculateChange);
chart.current?.unsubscribeCrosshairMove(handleCrosshair);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo. chart.remove() should clean up everything but from a quick look at the function documentation I don't know if it does so I left the unsubscribing in here just for safety..wdyt @thisconnect should I remove these?

@@ -101,10 +101,11 @@ export const Chart = ({
const ref = useRef<HTMLDivElement>(null);
const refToolTip = useRef<HTMLSpanElement>(null);
const chart = useRef<IChartApi>();
const chartInitialized = useRef(false);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we could also use chart.current but I like it like that, it is more clear even if its a bit redundant. but no strong opinion, let me know if I should remove this and use chart.current (!== undefined) to check if the chart was initialized.

@NicolaLS NicolaLS requested a review from thisconnect June 7, 2024 17:18
@NicolaLS NicolaLS force-pushed the refactor-functional-chart-III branch 2 times, most recently from 7a8cc75 to 1e241e8 Compare June 11, 2024 14:04
Convert the Chart class component in chart.tsx to a functional
component.
Refactor the charti initialization after the refactor to a functional
component so that it only initializes the chart when required and cleans
everything up properly when the component unmounts or the chart is
removed to re-initialize it. SQUASH in last commit
@NicolaLS NicolaLS force-pushed the refactor-functional-chart-III branch from 1e241e8 to 59fa1c2 Compare June 17, 2024 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants