@@ -27,18 +27,8 @@ import SettingsDialog from './SettingsDialog';
2727import ScrollToRecent from './ScrollToRecent' ;
2828
2929const Chart = React . forwardRef ( ( props : TChartProps , ref ) => {
30- const {
31- chart,
32- drawTools,
33- studies,
34- chartSetting,
35- chartType,
36- state,
37- loader,
38- chartAdapter,
39- crosshair,
40- timeperiod,
41- } = useStores ( ) ;
30+ const { chart, drawTools, studies, chartSetting, chartType, state, loader, chartAdapter, crosshair, timeperiod } =
31+ useStores ( ) ;
4232 const { chartId, init, destroy, isChartAvailable, chartContainerHeight, containerWidth } = chart ;
4333 const { setCrosshairState } = crosshair ;
4434 const { settingsDialog : studiesSettingsDialog , restoreStudies, activeItems } = studies ;
@@ -86,7 +76,7 @@ const Chart = React.forwardRef((props: TChartProps, ref) => {
8676
8777 React . useEffect ( ( ) => {
8878 updateProps ( props ) ;
89- } ) ;
79+ } , [ props , updateProps ] ) ;
9080
9181 const prevLang = usePrevious ( t . lang ) ;
9282 React . useEffect ( ( ) => {
@@ -96,7 +86,10 @@ const Chart = React.forwardRef((props: TChartProps, ref) => {
9686 // eslint-disable-next-line react-hooks/exhaustive-deps
9787 } , [ t . lang ] ) ;
9888
99- const defaultTopWidgets = ( ) => < ChartTitle /> ;
89+ // [AI]
90+ // Memoize defaultTopWidgets to prevent recreation on every render
91+ const defaultTopWidgets = React . useCallback ( ( ) => < ChartTitle /> , [ ] ) ;
92+ // [/AI]
10093
10194 const {
10295 id,
@@ -114,6 +107,110 @@ const Chart = React.forwardRef((props: TChartProps, ref) => {
114107 contracts_array = [ ] ,
115108 } = props ;
116109
110+ // [AI]
111+ // Create stable references that persist across renders for barriers
112+ const stableBarrierRef = React . useRef < typeof barriers > ( [ ] ) ;
113+ const lastBarrierHashRef = React . useRef < string > ( '' ) ;
114+
115+ // Use useEffect to update barriers when they actually change, avoiding useMemo dependency issues
116+ React . useEffect ( ( ) => {
117+ // Create a hash of all barrier properties that matter for rendering
118+ const barrierHash = barriers
119+ . map ( barrier => {
120+ if ( ! barrier ) return 'null' ;
121+
122+ const {
123+ high,
124+ low,
125+ color,
126+ lineStyle,
127+ shade,
128+ shadeColor,
129+ relative,
130+ draggable,
131+ hidePriceLines,
132+ hideBarrierLine,
133+ hideOffscreenLine,
134+ title,
135+ key,
136+ } = barrier ;
137+ return JSON . stringify ( {
138+ high,
139+ low,
140+ color,
141+ lineStyle,
142+ shade,
143+ shadeColor,
144+ relative,
145+ draggable,
146+ hidePriceLines,
147+ hideBarrierLine,
148+ hideOffscreenLine,
149+ title,
150+ key,
151+ } ) ;
152+ } )
153+ . join ( '|' ) ;
154+
155+ // Only update the stable reference if the barrier properties have actually changed
156+ if ( barrierHash !== lastBarrierHashRef . current ) {
157+ // Barrier data actually changed, updating stable reference
158+ lastBarrierHashRef . current = barrierHash ;
159+ stableBarrierRef . current = barriers . map ( barrier => {
160+ if ( ! barrier ) return barrier ;
161+
162+ // Create a plain object with barrier properties for the chart
163+ return {
164+ ...barrier ,
165+ high : barrier . high ,
166+ low : barrier . low ,
167+ color : barrier . color ,
168+ lineStyle : barrier . lineStyle ,
169+ shade : barrier . shade ,
170+ shadeColor : barrier . shadeColor ,
171+ relative : barrier . relative ,
172+ draggable : barrier . draggable ,
173+ hidePriceLines : barrier . hidePriceLines ,
174+ hideBarrierLine : barrier . hideBarrierLine ,
175+ hideOffscreenLine : barrier . hideOffscreenLine ,
176+ title : barrier . title ,
177+ key : barrier . key ,
178+ } ;
179+ } ) ;
180+ }
181+ } , [ barriers ] ) ;
182+
183+ // Return the stable reference directly without useMemo
184+ const stableBarriers = stableBarrierRef . current ;
185+
186+ // Memoize the entire barriers rendering to prevent re-renders when barriers haven't changed
187+ const memoizedBarriers = React . useMemo ( ( ) => {
188+ return stableBarriers . map ( ( { key, ...barr } , idx ) => (
189+ < Barrier
190+ key = { key || `barrier-${ idx } ` } // eslint-disable-line react/no-array-index-key
191+ high = { barr . high }
192+ low = { barr . low }
193+ color = { barr . color }
194+ lineStyle = { barr . lineStyle }
195+ shade = { barr . shade }
196+ shadeColor = { barr . shadeColor }
197+ relative = { barr . relative }
198+ draggable = { barr . draggable }
199+ hidePriceLines = { barr . hidePriceLines }
200+ hideBarrierLine = { barr . hideBarrierLine }
201+ hideOffscreenLine = { barr . hideOffscreenLine }
202+ title = { barr . title }
203+ onChange = { barr . onChange }
204+ hideOffscreenBarrier = { barr . hideOffscreenBarrier }
205+ isSingleBarrier = { barr . isSingleBarrier }
206+ opacityOnOverlap = { barr . opacityOnOverlap }
207+ showOffscreenArrows = { barr . showOffscreenArrows }
208+ foregroundColor = { barr . foregroundColor }
209+ />
210+ ) ) ;
211+ } , [ stableBarriers ] ) ;
212+ // [/AI]
213+
117214 const hasPosition = chartControlsWidgets && position && ! isMobile ;
118215 const TopWidgets = topWidgets || defaultTopWidgets ;
119216 // if there are any markers, then increase the subholder z-index
@@ -133,10 +230,23 @@ const Chart = React.forwardRef((props: TChartProps, ref) => {
133230 chartAdapter . updateContracts ( contracts_array ) ;
134231 } , [ contracts_array ] ) ;
135232
136- // to always show price info on mobile screen
137- if ( isMobile && crosshair . state !== 2 ) {
138- setCrosshairState ( 2 ) ;
139- }
233+ // [AI]
234+ // Move conditional logic to useEffect to prevent re-renders during render phase
235+ React . useEffect ( ( ) => {
236+ // to always show price info on mobile screen
237+ if ( isMobile && crosshair . state !== 2 ) {
238+ setCrosshairState ( 2 ) ;
239+ }
240+ } , [ isMobile , crosshair . state , setCrosshairState ] ) ;
241+
242+ // Memoize the dynamic style object to prevent recreation on every render
243+ const chartContainerStyle = React . useMemo (
244+ ( ) => ( {
245+ height : historical && chartContainerHeight && isMobile ? chartContainerHeight - 30 : chartContainerHeight ,
246+ } ) ,
247+ [ historical , chartContainerHeight , isMobile ]
248+ ) ;
249+ // [/AI]
140250
141251 return (
142252 < div
@@ -163,14 +273,7 @@ const Chart = React.forwardRef((props: TChartProps, ref) => {
163273 >
164274 < div className = 'ciq-chart-area' >
165275 < div className = { classNames ( 'ciq-chart' , { 'closed-chart' : isChartClosed } ) } >
166- < RenderInsideChart at = 'subholder' >
167- { barriers . map ( ( { key, ...barr } , idx ) => (
168- < Barrier
169- key = { `barrier-${ idx } ` } // eslint-disable-line react/no-array-index-key
170- { ...barr }
171- />
172- ) ) }
173- </ RenderInsideChart >
276+ < RenderInsideChart at = 'subholder' > { memoizedBarriers } </ RenderInsideChart >
174277 < RenderInsideChart at = 'subholder' >
175278 { ! isCandle && ! isSpline && isHighestLowestMarkerEnabled && < HighestLowestMarker /> }
176279 </ RenderInsideChart >
@@ -183,16 +286,7 @@ const Chart = React.forwardRef((props: TChartProps, ref) => {
183286 < TopWidgets />
184287 </ div >
185288 ) }
186- < div
187- className = 'chartContainer'
188- ref = { chartContainerRef }
189- style = { {
190- height :
191- historical && chartContainerHeight && isMobile
192- ? chartContainerHeight - 30
193- : chartContainerHeight ,
194- } }
195- >
289+ < div className = 'chartContainer' ref = { chartContainerRef } style = { chartContainerStyle } >
196290 < Crosshair />
197291 </ div >
198292 { enabledNavigationWidget && (
0 commit comments