Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 99011a2b49 fix: guard useObjectNameSingularFromPlural against missing metadata during schema refresh
https://sonarly.com/issue/34403?type=bug

When a workspace's data model changes (schema version mismatch), the metadata store refreshes and removes deleted objects. Components like ViewBarPageTitle re-render with stale URL params before the redirect to /not-found executes, causing an unhandled throw.

Fix: Replaced unsafe `useParams()` + `useObjectNameSingularFromPlural()` pattern with `useRecordIndexContextOrThrow()` in two ViewBar components that crash during metadata refresh:

1. **`ViewBarPageTitle.tsx`**: Removed `useParams()` and `useObjectNameSingularFromPlural` imports. Now gets `objectNameSingular` from the RecordIndex context (which is only rendered when the object metadata is valid).

2. **`useSortsFromQueryParams.ts`**: Same pattern — removed `useParams()` and `useObjectNameSingularFromPlural`, replaced with `useRecordIndexContextOrThrow()` to get the validated `objectNameSingular`.

This follows the exact same fix pattern already applied in commit `9b1e6d5a9d` to `QueryParamsFiltersEffect` and `useFiltersFromQueryParams`. These components are only ever rendered inside `RecordIndexContainerGater`, which guards rendering with a check that `objectMetadataItem` is defined — so the RecordIndex context is always populated when these components mount.
2026-05-05 10:00:55 +00:00
2 changed files with 5 additions and 14 deletions
@@ -1,18 +1,12 @@
import { useParams } from 'react-router-dom';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle';
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
export const ViewBarPageTitle = () => {
const { objectNamePlural } = useParams();
const { objectNameSingular } = useRecordIndexContextOrThrow();
const { currentView } = useGetCurrentViewOnly();
const { objectNameSingular } = useObjectNameSingularFromPlural({
objectNamePlural: objectNamePlural ?? '',
});
const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular });
const pageTitle = currentView?.name
@@ -2,19 +2,16 @@ import qs from 'qs';
import { useCallback, useMemo } from 'react';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { type RecordSort } from '@/object-record/record-sort/types/RecordSort';
import { sortUrlQueryParamsSchema } from '@/views/schemas/sortUrlQueryParamsSchema';
import { useParams, useSearchParams } from 'react-router-dom';
import { useSearchParams } from 'react-router-dom';
import { isDefined } from 'twenty-shared/utils';
import { type ViewSortDirection } from '~/generated-metadata/graphql';
export const useSortsFromQueryParams = () => {
const [searchParams] = useSearchParams();
const { objectNamePlural = '' } = useParams();
const { objectNameSingular } = useObjectNameSingularFromPlural({
objectNamePlural,
});
const { objectNameSingular } = useRecordIndexContextOrThrow();
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});