Skip to content

Commit 4cb9201

Browse files
committed
perf: improve the stepped form lookup performance
1 parent cc41985 commit 4cb9201

File tree

2 files changed

+38
-22
lines changed

2 files changed

+38
-22
lines changed

.changeset/five-items-melt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@formwerk/core': patch
3+
---
4+
5+
perf: improve the stepped form lookup performance

packages/core/src/useFormFlow/useFormFlow.ts

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { computed, nextTick, onMounted, provide, reactive, Ref, ref, toValue, shallowRef } from 'vue';
1+
import { computed, nextTick, provide, reactive, Ref, ref, toValue, shallowRef } from 'vue';
22
import { NoSchemaFormProps, useForm } from '../useForm';
33
import { FormObject, IssueCollection, Path } from '../types';
44
import { isObject, merge } from '../../../shared/src';
5-
import { cloneDeep } from '../utils/common';
5+
import { cloneDeep, isSSR } from '../utils/common';
66
import { FormFlowContextKey, SegmentMetadata, SegmentRegistrationMetadata, StepIdentifier } from './types';
77
import { asConsumableData, ConsumableData } from '../useForm/useFormActions';
88
import { createEventDispatcher } from '../utils/events';
@@ -25,6 +25,10 @@ export interface GoToPredicateContext {
2525
relativeDistance: number;
2626
}
2727

28+
export interface RenderedSegment {
29+
id: string;
30+
}
31+
2832
export function useFormFlow<TInput extends FormObject = FormObject>(_props?: FormFlowProps<TInput>) {
2933
const currentSegmentId = ref<string>();
3034
const formElement = shallowRef<HTMLElement>();
@@ -33,9 +37,7 @@ export function useFormFlow<TInput extends FormObject = FormObject>(_props?: For
3337
const form = useForm(_props);
3438
const segments = ref<SegmentMetadata[]>([]);
3539

36-
function getCurrentSegment() {
37-
return segments.value.find(segment => segment.id === currentSegmentId.value);
38-
}
40+
const rawCurrentSegment = computed(() => segments.value.find(segment => segment.id === currentSegmentId.value));
3941

4042
const [dispatchActiveSegmentChange, onActiveSegmentChange] = createEventDispatcher<{
4143
data: ConsumableData<TInput>;
@@ -44,7 +46,7 @@ export function useFormFlow<TInput extends FormObject = FormObject>(_props?: For
4446
}>('activeSegmentChange');
4547

4648
function beforeSegmentChange(nextSegment: SegmentMetadata, applyChange: () => void) {
47-
const currentSegment = getCurrentSegment();
49+
const currentSegment = rawCurrentSegment.value;
4850
if (currentSegment) {
4951
saveValues();
5052
dispatchActiveSegmentChange({
@@ -59,7 +61,7 @@ export function useFormFlow<TInput extends FormObject = FormObject>(_props?: For
5961
}
6062

6163
function saveValues() {
62-
const currentSegment = getCurrentSegment();
64+
const currentSegment = rawCurrentSegment.value;
6365
if (!currentSegment) {
6466
return;
6567
}
@@ -74,20 +76,27 @@ export function useFormFlow<TInput extends FormObject = FormObject>(_props?: For
7476
}
7577

7678
provide(FormFlowContextKey, {
77-
isSegmentActive: (segmentId: string) => getCurrentSegment()?.id === segmentId,
78-
registerSegment: (metadata: SegmentRegistrationMetadata) =>
79+
isSegmentActive: (segmentId: string) => rawCurrentSegment.value?.id === segmentId,
80+
registerSegment: (metadata: SegmentRegistrationMetadata) => {
7981
segments.value.push({
8082
id: metadata.id,
8183
name: () => toValue(metadata.name),
8284
// The first segment is always visited.
8385
visited: segments.value.length === 0,
8486
submitted: false,
8587
getValue: () => segmentValuesMap.value.get(metadata.id)?.values,
86-
}),
88+
});
89+
90+
// Activate the first segment if there is only one by default
91+
// Fixes SSR #231
92+
if (segments.value.length === 1) {
93+
currentSegmentId.value = metadata.id;
94+
}
95+
},
8796
});
8897

8998
const currentSegment = computed(() => {
90-
const curr = getCurrentSegment();
99+
const curr = rawCurrentSegment.value;
91100
if (!curr) {
92101
return null;
93102
}
@@ -101,7 +110,7 @@ export function useFormFlow<TInput extends FormObject = FormObject>(_props?: For
101110
return -1;
102111
}
103112

104-
return getDomSegments().findIndex(segment => segment.dataset.formSegmentId === current.id);
113+
return getRenderedSegments().findIndex(segment => segment.id === current.id);
105114
});
106115

107116
const isLastSegment = computed(() => currentSegmentIndex.value === segments.value.length - 1);
@@ -125,7 +134,7 @@ export function useFormFlow<TInput extends FormObject = FormObject>(_props?: For
125134

126135
async function restoreSegmentValues() {
127136
await nextTick();
128-
const currentSegment = getCurrentSegment();
137+
const currentSegment = rawCurrentSegment.value;
129138
// restore field values
130139
if (currentSegment && hasState(currentSegment.id)) {
131140
const state = segmentValuesMap.value.get(currentSegment.id) as StepState<TInput>;
@@ -136,14 +145,20 @@ export function useFormFlow<TInput extends FormObject = FormObject>(_props?: For
136145
}
137146
}
138147

139-
function getDomSegments() {
140-
return Array.from(formElement.value?.querySelectorAll(`[data-form-segment-id]`) || []) as HTMLElement[];
148+
function getRenderedSegments(): RenderedSegment[] {
149+
if (isSSR) {
150+
return segments.value.map(segment => ({ id: segment.id }));
151+
}
152+
153+
return Array.from(formElement.value?.querySelectorAll(`[data-form-segment-id]`) || []).map(segment => ({
154+
id: (segment as HTMLElement).dataset.formSegmentId ?? '',
155+
}));
141156
}
142157

143158
function getSegmentAt(idx: number): { idx: number; segment: SegmentMetadata } | undefined {
144-
const domSegments = getDomSegments();
159+
const domSegments = getRenderedSegments();
145160

146-
const i = segments.value.findIndex(segment => segment.id === domSegments[idx].dataset.formSegmentId);
161+
const i = segments.value.findIndex(segment => segment.id === domSegments[idx].id);
147162
if (i === -1) {
148163
return undefined;
149164
}
@@ -225,10 +240,6 @@ export function useFormFlow<TInput extends FormObject = FormObject>(_props?: For
225240
}
226241
}
227242

228-
onMounted(() => {
229-
moveRelative(0);
230-
});
231-
232243
return {
233244
form,
234245

@@ -275,7 +286,7 @@ export function useFormFlow<TInput extends FormObject = FormObject>(_props?: For
275286
/**
276287
* Returns the DOM segments.
277288
*/
278-
getDomSegments,
289+
getRenderedSegments,
279290

280291
/**
281292
* The segments.

0 commit comments

Comments
 (0)