# Sub-Pages — Data Hook > **Part of:** [Sub-Pages guide](../sub-pages.md) > **See also:** [Tabs Component](03-tabs-component.md) | [Settings Tab](04-settings-tab.md) ## What is a Data Hook? A data hook (`useSettingsData.tsx`) encapsulates data-fetching logic for a sub-page's Settings tab. ## Prerequisites - **RPC service** with `*_show` endpoint — see [main-pages/09-rpc-service.md](../main-pages/09-rpc-service.md) - **Entity type interface** in `globalDataTypes.ts` - **Entity utils** with `apiTo` — see [main-pages/08-delete-modal-and-utils.md](../main-pages/08-delete-modal-and-utils.md) > ⚠️ **CRITICAL:** The RPC `*_show` endpoint **must** use `apiTo` in `transformResponse`. Without this, form fields will be **read-only**. ## Location and Naming - **File:** `src/hooks/useSettingsData.tsx` - **Hook function:** `useSettings` ## What the Hook Returns ```tsx type SettingsData = { isLoading: boolean; isFetching: boolean; : Partial<>; original: Partial<>; metadata: Metadata; modified: boolean; setModified: (value: boolean) => void; set: (: Partial<>) => void; refetch: () => void; resetValues: () => void; modifiedValues: () => Partial<>; }; ``` ## Implementation Template ```tsx const useSettings = (: string): SettingsData => { // 1. API Calls const metadataQuery = useGetObjectMetadataQuery(); const metadata = metadataQuery.data || {}; const Query = useShowQuery(); // 2. Local State const [modified, setModified] = React.useState(false); const [, set] = React.useState>>({}); const [original, setOriginal] = React.useState>>({}); // 3. Sync API Data (include fulfilledTimeStamp for cache detection) React.useEffect(() => { if (Query.data && !Query.isFetching) { set({ ...Query.data }); setOriginal({ ...Query.data }); } }, [Query.data, Query.isFetching, Query.fulfilledTimeStamp]); // 4. Change Detection React.useEffect(() => { if (!original) return; let isModified = false; for (const [key, value] of Object.entries()) { const originalVal = original[key]; const isDifferent = Array.isArray(value) ? JSON.stringify(originalVal) !== JSON.stringify(value) : originalVal !== value; if (isDifferent) { isModified = true; break; } } setModified(isModified); }, [, original]); // 5. Modified Values Helper const getModifiedValues = (): Partial<> => { const result = {}; for (const [key, value] of Object.entries()) { const originalVal = original[key]; const isDifferent = Array.isArray(value) ? JSON.stringify(originalVal) !== JSON.stringify(value) : originalVal !== value; if (isDifferent) result[key] = value; } return result; }; // 6. Reset Values (CRITICAL: update original to match current) const onResetValues = () => { setOriginal({ ... }); setModified(false); }; // 7. Return return { isLoading: metadataQuery.isLoading || Query.isLoading, isFetching: Query.isFetching, modified, setModified, metadata, resetValues: onResetValues, original, set, refetch: Query.refetch, , modifiedValues: getModifiedValues, }; }; export { useSettings }; ``` ## Reset Values — Critical Note > ⚠️ **CRITICAL:** `resetValues` must update `original` to match ``. Otherwise, change detection will recalculate `modified` back to `true`. See [Troubleshooting](13-troubleshooting.md). ## Fetching Options for Select Fields ```tsx const usersQuery = useGetActiveUsersQuery(); const userOptions = React.useMemo(() => { if (!usersQuery.data) return []; return usersQuery.data.map((user) => Array.isArray(user.uid) ? user.uid[0] : user.uid); }, [usersQuery.data]); // Include in loading state and return isLoading: metadataQuery.isLoading || Query.isLoading || usersQuery.isLoading, return { ...otherProps, userOptions }; ``` ## API-Specific Limitations Not all API commands support the same options. Verify by checking: - [FreeIPA API docs](https://freeipa.readthedocs.io/en/latest/api/) — General documentation - [API.txt](https://codeberg.org/freeipa/freeipa/src/branch/master/API.txt) — Complete command specifications (options, inputs, outputs for every IPA command). This file is auto-generated from the FreeIPA source tree and updated with every release, so it is always in sync with the actual server API. | Entity | `_find` limitations | `_show` limitations | |--------|---------------------|---------------------| | `delegation` | No `sizelimit` | No `rights` | When unsupported option used, API returns: `{"error": {"code": 3005, "message": "Unknown option: rights"}}` ## Required Imports ```tsx import React from "react"; import { useGetObjectMetadataQuery } from "src/services/rpc"; import { useShowQuery } from "src/services/rpc"; import { , Metadata } from "src/utils/datatypes/globalDataTypes"; ``` ## Examples | Complexity | Example File | |------------|--------------| | Simple | `src/hooks/useIdpRefSettingsData.tsx` | | With transformation | `src/hooks/useDnsZonesData.tsx` |