Skip to content

Commit

Permalink
fix: refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
RichardLindhout committed May 20, 2023
1 parent b5a9319 commit 59bb546
Show file tree
Hide file tree
Showing 14 changed files with 343 additions and 214 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-use-form",
"version": "0.1.0",
"version": "1.0.0",
"description": "test",
"main": "lib/commonjs/index",
"module": "lib/module/index",
Expand Down
2 changes: 1 addition & 1 deletion src/FormContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import type { IndexerType, ReferencerType } from './useFormState';
import type { IndexerType, ReferencerType } from './types';
// import { MutableRefObject } from 'react';

export type FormContextType = {
Expand Down
18 changes: 14 additions & 4 deletions src/inputs/numberRaw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@ import type {
DotNestedKeys,
FormTextInputProps,
FieldsLastCharacters,
GetFieldType,
} from '../types';
import { deepGet, deepSet } from '../objectPath';
import { isEmptyNumber, reverse } from '../utils';
import * as React from 'react';
import useRefState from '../useRefState';
import type { ReferencedCallback } from '../types';

export function useNumberRaw<T>({ locale }: { locale: string }) {
export function useNumberRaw<T>({
locale,
referencedCallback,
}: {
locale: string;
referencedCallback: ReferencedCallback;
}) {
const [lastCharacters, setLastCharacters] = useRefState<
FieldsLastCharacters<T>
>({});
Expand All @@ -27,9 +35,11 @@ export function useNumberRaw<T>({ locale }: { locale: string }) {
}, [locale]);
return <K extends DotNestedKeys<T>>(
k: K,
h?: Customizing<T, K>
): FormTextInputProps => {
const deepValue = deepGet(values.current, k) as number;
h: Customizing<T, K> | undefined,
values: T,
changeValue: (k: K, v: GetFieldType<T, K>, h?: Customizing<T, K>) => void
): Pick<FormTextInputProps, 'onChangeText' | 'value'> => {
const deepValue = deepGet(values, k) as number;
const value = `${isEmptyNumber(deepValue) ? '' : deepValue}`.replace(
'.',
separationCharacter
Expand Down
141 changes: 69 additions & 72 deletions src/inputs/useInputs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
BooleanUtility,
import type {
BaseCustomizing,
Customizing,
CustomizingRaw,
DotNestedKeys,
Expand All @@ -9,8 +9,9 @@ import {
FormOptions,
FormTextInputProps,
GetFieldType,
ReferencedCallback,
} from '../types';
import { removeEmpty, useReferencedCallback } from '../utils';
import { removeEmpty, useLatest } from '../utils';
import { deepGet, deepSet } from '../objectPath';
import { useNumberRaw } from './numberRaw';
import {
Expand All @@ -20,55 +21,49 @@ import {
TextInputProps,
} from 'react-native';
import * as React from 'react';
import useRefState from '../useRefState';
import useErrors from '../useErrors';
import type { UseErrorsReturnType } from '../useErrors';
import type { UseLayoutReturnType } from '../useLayout';
import type { FormContextType } from '../FormContext';
import type { UseValuesReturnType } from '../useValues';
import type { UseTouchedReturnType } from '../useTouched';
import type { UseFocusedOnceReturnType } from '../useFocusedOnce';
import type { UseWasSubmittedReturnType } from '../useWasSubmitted';

function useTouched<T>() {
const [touched, sTouched] = useRefState<BooleanUtility<T>>({});
const setTouched = React.useCallback(
<K extends DotNestedKeys<T>>(k: K, v: boolean) => {
sTouched((p) => deepSet(p, k, v) as any);
},
[sTouched]
);
return { touched, setTouched };
}
export function useInputs<T>(options: FormOptions<T> | undefined): {
inputs: FormInputsType<T>;
focusedOnce: BooleanUtility<T>;
} {
const referencedCallback = useReferencedCallback();
const { touched, setTouched } = useTouched<T>();
const [values, setValues] = useRefState<T>(initialState);
const { errors, setError, checkError, setErrors } = useErrors<T>({ locale });
export type UseInputsReturnType<T> = ReturnType<typeof useInputs<T>>;

export function useInputs<T>({
options,
locale,
context,
referencedCallback,
wasSubmitted: { wasSubmitted },
error: { checkError, updateHandler: updateErrorHandler, hasError },
layout: { onLayoutKey },
value: { values, setValues },
touch: { touched, setTouched },
focusedOnce: { focusedOnce, setFocusedOnce },
}: {
options: FormOptions<T> | undefined;
locale: string;
context: FormContextType & {
formIndex: number;
};
referencedCallback: ReferencedCallback;
wasSubmitted: UseWasSubmittedReturnType;
error: UseErrorsReturnType<T>;
layout: UseLayoutReturnType<T>;
value: UseValuesReturnType<T>;
touch: UseTouchedReturnType<T>;
focusedOnce: UseFocusedOnceReturnType<T>;
}) {
const onChange = useLatest(options?.onChange);
const enhance = useLatest(options?.enhance);

type InputT<ReturnType> = <K extends DotNestedKeys<T>>(
k: K,
h: Customizing<T, K> | undefined
) => ReturnType;

const [focusedOnce, sFocusedOnce] = useRefState<BooleanUtility<T>>({});
const setFocusedOnce = React.useCallback(
<K extends DotNestedKeys<T>>(k: K, v: boolean) => {
sFocusedOnce((p) => deepSet(p, k, v) as any);
},
[sFocusedOnce]
);

const hasError = React.useCallback(
<K extends DotNestedKeys<T>>(k: K): boolean => {
const isTouched = deepGet(touched.current, k);
const isFocusedOnce = deepGet(focusedOnce.current, k);
const error = deepGet(errors.current, k);
if ((isTouched && isFocusedOnce) || wasSubmitted.current) {
const noError = error === false || errors.current === undefined;
return !noError;
}
return false;
},
[errors, focusedOnce, touched, wasSubmitted]
);

const changeValue = React.useCallback(
<K extends DotNestedKeys<T>>(
k: K,
Expand Down Expand Up @@ -97,13 +92,13 @@ export function useInputs<T>(options: FormOptions<T> | undefined): {
onChange.current?.(enhancedNewValues, {
touched: touched.current,
focusedOnce: focusedOnce.current,
errors: errors.current,
// errors: errors.current,
});
},
[
checkError,
enhance,
errors,
// errors,
focusedOnce,
onChange,
setTouched,
Expand All @@ -121,16 +116,19 @@ export function useInputs<T>(options: FormOptions<T> | undefined): {
setFocusedOnce(k, true);
}
);
const baseProps: InputT<FormInputBaseProps> = (k, h) =>
removeEmpty({
...ctx.referencer(k, ctx.formIndex),
const baseProps: InputT<FormInputBaseProps> = (k, h) => {
updateErrorHandler(k, h as BaseCustomizing<K, string>);
return removeEmpty({
...context.referencer(k, context.formIndex),
testID: k,
onLayout: layout(k, h),
onLayout: onLayoutKey(k),
onBlur: blur(k, h),
error: hasError(k),
errorMessage: deepGet(errors.current, k),
errorMessage: 'TODO',
// errorMessage: deepGet(errors.current, k),
label: h?.label,
});
};

const text: InputT<FormTextInputProps> = (k, h) => ({
...baseProps(k, h),
Expand All @@ -141,7 +139,7 @@ export function useInputs<T>(options: FormOptions<T> | undefined): {
),
});

const numberRawCreator = useNumberRaw<T>();
const numberRawCreator = useNumberRaw<T>({ locale, referencedCallback });
const numberRaw: InputT<FormTextInputProps> = (k, h) => ({
...baseProps(k, h),
...numberRawCreator(k, h, values.current, changeValue),
Expand Down Expand Up @@ -221,8 +219,6 @@ export function useInputs<T>(options: FormOptions<T> | undefined): {
autoCorrect: false,
selectTextOnFocus: Platform.OS !== 'web',
label: h?.label,
errorMessage: deepGet(errors.current, k),
error: hasError(k),
});

const email: InputT<FormTextInputProps> = (k, h) => ({
Expand Down Expand Up @@ -254,24 +250,25 @@ export function useInputs<T>(options: FormOptions<T> | undefined): {
[changeValue]
);

// @ts-expect-error
const inputs: FormInputsType<T> = {
text,
number,
decimal,
numberText,
decimalText,
postalCode,
streetAddress,
city,
telephone,
name,
username,
password,
email,
raw,
};
return {
inputs: {
text,
number,
decimal,
numberText,
decimalText,
postalCode,
streetAddress,
city,
telephone,
name,
username,
password,
email,
raw,
},
inputs: inputs as any, // any for infinite error loop typescript
setField,
focusedOnce: focusedOnce.current,
};
}
5 changes: 2 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
TextInputProps,
} from 'react-native';
import type { TextInput } from 'react-native';
import { ScrollView, View } from 'react-native';
import type { ScrollView, View } from 'react-native';

type GetIndexedField<T, K> = K extends keyof NonNullable<T>
? NonNullable<T>[K]
Expand Down Expand Up @@ -189,7 +189,7 @@ export type FormInputRawProps<V> = {
onChange: (v: V) => void;
} & FormInputBaseProps;

interface BaseCustomizing<T, K extends DotNestedKeys<T>> {
export interface BaseCustomizing<T, K extends DotNestedKeys<T>> {
label?: string;
required?: boolean;
shouldFollowRegexes?: {
Expand Down Expand Up @@ -220,7 +220,6 @@ export interface CustomizingRaw<T, K extends DotNestedKeys<T>>

type FormRawType<T> = <K extends DotNestedKeysWithRoot<T>>(
key: K,
// @ts-ignore
handlers?: CustomizingRaw<T, K>
) => FormRawProps<GetFieldType<T, K>>;

Expand Down
19 changes: 4 additions & 15 deletions src/useCheckError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,13 @@ import type {
} from './types';
import { getTranslation } from './translations/utils';

export default function useCheckError<T>({
locale,
setError,
}: {
locale: string;
setError: any;
}) {
export default function useCheckError<T>({ locale }: { locale: string }) {
return React.useCallback(
<K extends DotNestedKeys<T>>(
k: K,
h: Customizing<T, K> | CustomizingRaw<T, K> | undefined,
v: GetFieldType<T, K>,
allV: T,
initial?: boolean
allV: T
) => {
let err: boolean | string | undefined;

Expand Down Expand Up @@ -69,12 +62,8 @@ export default function useCheckError<T>({
err = h.validate?.(v, allV);
}
}
setError(
k,
err === true || err === undefined || err === null ? false : err,
initial
);
return err === true || err === undefined || err === null ? false : err;
},
[locale, setError]
[locale]
);
}
Loading

0 comments on commit 59bb546

Please sign in to comment.