From d431122700d12b1cc8b6c8e79310a31f57f7f379 Mon Sep 17 00:00:00 2001 From: Miroslav Petrik Date: Fri, 17 Oct 2025 12:23:16 +0200 Subject: [PATCH 1/6] fix: linter issues --- src/atoms/extendAtom.ts | 4 ++-- src/fields/list-field/listField.ts | 11 ++++------- src/hooks/use-required-props/useRequiredProps.ts | 5 +++-- .../use-select-field-props/useSelectFieldProps.ts | 9 ++++----- src/scenarios/StoryForm.tsx | 4 ++-- 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/atoms/extendAtom.ts b/src/atoms/extendAtom.ts index 80f984c..8054a03 100644 --- a/src/atoms/extendAtom.ts +++ b/src/atoms/extendAtom.ts @@ -1,11 +1,11 @@ -import type { Getter, PrimitiveAtom } from "jotai"; +import type { Getter, Atom } from "jotai"; import { atomWithDefault } from "jotai/utils"; export const extendAtom = < T extends Record, E extends Record, >( - baseAtom: PrimitiveAtom, + baseAtom: Atom, makeAtoms: (cfg: T, get: Getter) => E, ) => { const extended = atomWithDefault((get) => { diff --git a/src/fields/list-field/listField.ts b/src/fields/list-field/listField.ts index 461452a..b7142bb 100644 --- a/src/fields/list-field/listField.ts +++ b/src/fields/list-field/listField.ts @@ -68,18 +68,15 @@ export const listField = ({ }), ); - const listFieldAtom = extendAtom( - listAtom({ ...config, validate }) as any, - () => ({ - required: requiredAtom, - }), - ) as unknown as RequiredListField; + const listFieldAtom = extendAtom(listAtom({ ...config, validate }), () => ({ + required: requiredAtom, + })) as unknown as RequiredListField; listFieldAtom.optional = (readRequired: ReadRequired = () => false) => { const { validate, requiredAtom } = makeOptional(readRequired); const optionalZodFieldAtom = extendAtom( - listAtom({ ...config, validate }) as any, + listAtom({ ...config, validate }), () => ({ required: requiredAtom }), ) as unknown as OptionalListField; diff --git a/src/hooks/use-required-props/useRequiredProps.ts b/src/hooks/use-required-props/useRequiredProps.ts index 23a4c72..a849558 100644 --- a/src/hooks/use-required-props/useRequiredProps.ts +++ b/src/hooks/use-required-props/useRequiredProps.ts @@ -1,5 +1,6 @@ -import { useAtomValue, useSetAtom } from "jotai"; import { useMemo } from "react"; +import { useAtomValue, useSetAtom } from "jotai"; +import type { ZodAny } from "zod"; import { OptionalZodField, ZodField } from "../../fields/zod-field/zodField"; @@ -30,7 +31,7 @@ export const useRequiredProps = ({ ); }; -export const useRequiredActions = (fieldAtom: OptionalZodField) => { +export const useRequiredActions = (fieldAtom: OptionalZodField) => { const field = useAtomValue(fieldAtom); const setRequired = useSetAtom(field.required); diff --git a/src/hooks/use-select-field-props/useSelectFieldProps.ts b/src/hooks/use-select-field-props/useSelectFieldProps.ts index 62c3f06..cf67855 100644 --- a/src/hooks/use-select-field-props/useSelectFieldProps.ts +++ b/src/hooks/use-select-field-props/useSelectFieldProps.ts @@ -1,6 +1,6 @@ -import { UseFieldOptions } from "form-atoms"; -import { useAtomValue } from "jotai"; import { ChangeEvent, useCallback, useMemo } from "react"; +import { useAtomValue } from "jotai"; +import type { UseFieldOptions } from "form-atoms"; import { FieldProps, type UseOptionsProps, useFieldProps } from ".."; import type { ZodField, ZodFieldValue } from "../../fields"; @@ -8,7 +8,7 @@ import type { ZodField, ZodFieldValue } from "../../fields"; /** * This restricts ZodField to have optional schema defaulting to 'undefined of required schema'. */ -export type SelectField = ZodField; +export type SelectField = ZodField; export type SelectFieldProps< Option, @@ -31,8 +31,7 @@ export const useSelectFieldProps = ( ) => { const atom = useAtomValue(field); const fieldValue = useAtomValue(atom.value); - // TODO: getValue should be useMemo dependency, currently we asume that it is stable - const values = useMemo(() => options.map(getValue), [options]); + const values = useMemo(() => options.map(getValue), [options, getValue]); const value = useMemo(() => values.indexOf(fieldValue), [fieldValue, values]); const getEventValue = useCallback( diff --git a/src/scenarios/StoryForm.tsx b/src/scenarios/StoryForm.tsx index 90ec5f0..9e39e0c 100644 --- a/src/scenarios/StoryForm.tsx +++ b/src/scenarios/StoryForm.tsx @@ -1,5 +1,5 @@ import { action } from "storybook/actions"; -import { Meta, StoryObj } from "@storybook/react"; +import type { Meta, StoryObj } from "@storybook/react"; import { FormAtom, FormFields, formAtom, useFormActions } from "form-atoms"; import { useMemo } from "react"; @@ -20,7 +20,7 @@ export const StoryForm = ({ children, required = true, }: Props) => { - const form = useMemo(() => formAtom(fields), []); + const form = useMemo(() => formAtom(fields), [fields]); const { reset, submit } = useFormActions(form); return ( From efcb4a2e5dce30763f307ace5f51295b21e94594 Mon Sep 17 00:00:00 2001 From: Miroslav Petrik Date: Fri, 17 Oct 2025 12:44:36 +0200 Subject: [PATCH 2/6] fix: useFieldProps store from options --- .../useClearInputAction.ts | 1 + src/hooks/use-field-props/useFieldProps.ts | 23 ++++++++++--------- src/hooks/use-options/useOptions.tsx | 3 +-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/hooks/use-clear-input-action/useClearInputAction.ts b/src/hooks/use-clear-input-action/useClearInputAction.ts index c7855fb..c4a70e3 100644 --- a/src/hooks/use-clear-input-action/useClearInputAction.ts +++ b/src/hooks/use-clear-input-action/useClearInputAction.ts @@ -13,6 +13,7 @@ export const useClearInputAction = ( () => ({ clear() { if (ref) { + // eslint-disable-next-line react-hooks/immutability ref.value = ""; } }, diff --git a/src/hooks/use-field-props/useFieldProps.ts b/src/hooks/use-field-props/useFieldProps.ts index f6db0db..f1b3e00 100644 --- a/src/hooks/use-field-props/useFieldProps.ts +++ b/src/hooks/use-field-props/useFieldProps.ts @@ -1,6 +1,6 @@ -import { UseFieldOptions, useField } from "form-atoms"; +import { ChangeEvent, useMemo, startTransition } from "react"; import { useAtomValue, useSetAtom } from "jotai"; -import { ChangeEvent, useMemo, useTransition } from "react"; +import { UseAtomOptions, useField } from "form-atoms"; import { ZodField, ZodFieldValue } from "../../fields"; @@ -16,16 +16,15 @@ export function useFieldProps< value: ZodFieldValue, ) => ZodFieldValue : never, - options?: UseFieldOptions>, + options?: UseAtomOptions, ) { const { actions, state } = useField>(fieldAtom, options); - const field = useAtomValue(fieldAtom); - const name = useAtomValue(field.name); - const required = useAtomValue(field.required); - const validationCount = useAtomValue(field._validateCount); - const validate = useSetAtom(field.validate); - const ref = useSetAtom(field.ref); - const [, startTransition] = useTransition(); + const field = useAtomValue(fieldAtom, options); + const name = useAtomValue(field.name, options); + const required = useAtomValue(field.required, options); + const validationCount = useAtomValue(field._validateCount, options); + const validate = useSetAtom(field.validate, options); + const ref = useSetAtom(field.ref, options); const ariaInvalid = state.validateStatus === "invalid"; // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-invalid @@ -62,10 +61,12 @@ export function useFieldProps< actions, name, required, - validationCount, getEventValue, ref, validate, + ariaInvalid, + fieldAtom, + requiredAriaInvalid, ], ); } diff --git a/src/hooks/use-options/useOptions.tsx b/src/hooks/use-options/useOptions.tsx index ec6174f..83b1a59 100644 --- a/src/hooks/use-options/useOptions.tsx +++ b/src/hooks/use-options/useOptions.tsx @@ -1,8 +1,7 @@ -import { FieldAtom } from "form-atoms"; import { ReactNode, useMemo } from "react"; export type UseOptionsProps