diff --git a/apps/client/src/common/components/view-params-editor/ViewParamsEditor.tsx b/apps/client/src/common/components/view-params-editor/ViewParamsEditor.tsx
index bd5e9b2c0..d30da2fef 100644
--- a/apps/client/src/common/components/view-params-editor/ViewParamsEditor.tsx
+++ b/apps/client/src/common/components/view-params-editor/ViewParamsEditor.tsx
@@ -1,4 +1,4 @@
-import { FormEvent, useEffect } from 'react';
+import { FormEvent, memo, useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import {
Button,
@@ -25,8 +25,9 @@ interface EditFormDrawerProps {
viewOptions: ViewOption[];
}
-// TODO: this is a good candidate for memoisation, but needs the paramFields to be stable
-export default function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) {
+export default memo(ViewParamsEditor);
+
+function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) {
const [searchParams, setSearchParams] = useSearchParams();
const { data: viewSettings } = useViewSettings();
diff --git a/apps/client/src/features/operator/Operator.tsx b/apps/client/src/features/operator/Operator.tsx
index 1298abcee..57d8abf44 100644
--- a/apps/client/src/features/operator/Operator.tsx
+++ b/apps/client/src/features/operator/Operator.tsx
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useRef, useState } from 'react';
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { isOntimeEvent, OntimeEvent, SupportedEntry } from 'ontime-types';
import { getFirstEventNormal, getLastEventNormal } from 'ontime-utils';
@@ -107,6 +107,10 @@ export default function Operator() {
const missingData = !data || !customFields || !projectData;
const isLoading = status === 'pending' || customFieldStatus === 'pending' || projectDataStatus === 'pending';
+ // gather option data
+ const defaultFormat = getDefaultFormat(settings?.timeFormat);
+ const operatorOptions = useMemo(() => getOperatorOptions(customFields, defaultFormat), [customFields, defaultFormat]);
+
if (missingData || isLoading) {
return ;
}
@@ -121,8 +125,6 @@ export default function Operator() {
const main = searchParams.get('main') as keyof TitleFields | null;
const secondary = searchParams.get('secondary');
- const defaultFormat = getDefaultFormat(settings?.timeFormat);
- const operatorOptions = getOperatorOptions(customFields, defaultFormat);
let isPast = Boolean(featureData.selectedEventId);
const hidePast = isStringBoolean(searchParams.get('hidepast'));
diff --git a/apps/client/src/features/viewers/clock/Clock.tsx b/apps/client/src/features/viewers/clock/Clock.tsx
index b0a739cdf..b5454caa3 100644
--- a/apps/client/src/features/viewers/clock/Clock.tsx
+++ b/apps/client/src/features/viewers/clock/Clock.tsx
@@ -1,3 +1,4 @@
+import { useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { ProjectData, Settings } from 'ontime-types';
@@ -112,7 +113,7 @@ export default function Clock(props: ClockProps) {
const clean = clock.replace('/:/g', '');
const defaultFormat = getDefaultFormat(settings?.timeFormat);
- const clockOptions = getClockOptions(defaultFormat);
+ const clockOptions = useMemo(() => getClockOptions(defaultFormat), [defaultFormat]);
return (
getLowerThirdOptions(customFields), [customFields]);
+
return (
-
+
getBackstageOptions(defaultFormat, customFields),
+ [defaultFormat, customFields],
+ );
return (
diff --git a/apps/client/src/views/countdown/Countdown.tsx b/apps/client/src/views/countdown/Countdown.tsx
index 69055f559..4e7db1170 100644
--- a/apps/client/src/views/countdown/Countdown.tsx
+++ b/apps/client/src/views/countdown/Countdown.tsx
@@ -1,4 +1,4 @@
-import { useState } from 'react';
+import { useMemo, useState } from 'react';
import { IoAdd } from 'react-icons/io5';
import {
CustomFields,
@@ -63,7 +63,10 @@ export default function Countdown({
// gather option data
const defaultFormat = getDefaultFormat(settings?.timeFormat);
- const countdownOptions = getCountdownOptions(defaultFormat, customFields, subscriptions);
+ const countdownOptions = useMemo(
+ () => getCountdownOptions(defaultFormat, customFields, subscriptions),
+ [defaultFormat, customFields, subscriptions],
+ );
return (
diff --git a/apps/client/src/views/timeline/TimelinePage.tsx b/apps/client/src/views/timeline/TimelinePage.tsx
index bc444d8b2..1d6980bf2 100644
--- a/apps/client/src/views/timeline/TimelinePage.tsx
+++ b/apps/client/src/views/timeline/TimelinePage.tsx
@@ -45,7 +45,7 @@ export default function TimelinePage(props: TimelinePageProps) {
// populate options
const defaultFormat = getDefaultFormat(settings?.timeFormat);
- const progressOptions = getTimelineOptions(defaultFormat);
+ const progressOptions = useMemo(() => getTimelineOptions(defaultFormat), [defaultFormat]);
const titleNow = now?.title ?? '-';
const dueText = getLocalizedString('timeline.due').toUpperCase();