From 3a0c5772c1e75cb043df3327a25ee97b0822c6e8 Mon Sep 17 00:00:00 2001 From: Alexandre Philibeaux Date: Mon, 5 Dec 2022 12:15:09 +0000 Subject: [PATCH] feat(hook): add useOnFieldChange --- src/hooks/__tests__/useOnFieldChange.spec.tsx | 113 ++++++++++++++++++ src/hooks/index.ts | 1 + src/hooks/useOnFieldChange.ts | 29 +++++ src/index.ts | 2 +- 4 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/hooks/__tests__/useOnFieldChange.spec.tsx create mode 100644 src/hooks/useOnFieldChange.ts diff --git a/src/hooks/__tests__/useOnFieldChange.spec.tsx b/src/hooks/__tests__/useOnFieldChange.spec.tsx new file mode 100644 index 00000000..cfa90e77 --- /dev/null +++ b/src/hooks/__tests__/useOnFieldChange.spec.tsx @@ -0,0 +1,113 @@ +import { ThemeProvider } from '@emotion/react' +import { theme as lightTheme } from '@scaleway/ui' +import { renderHook } from '@testing-library/react' +import type { ReactElement } from 'react' +import { CheckboxField, Form, TextBoxField } from '../../components' +import { mockErrors } from '../../mocks' +import { useOnFieldChange } from '../useOnFieldChange' + +type FormValues = { + textBoxName: string + check: boolean +} + +type Wrapers = { + children: ReactElement + initialValues: FormValues +} + +const initial = { + textBoxName: 'test', + check: true, +} + +const updated = { + textBoxName: 'updated', + check: false, +} + +const Wrapper = ({ children, initialValues }: Wrapers) => ( + + + initialValues={initialValues} + errors={mockErrors} + onRawSubmit={() => {}} + > + {children} + + + + +) + +describe('useOnFieldChange', () => { + test('should render correctly', () => { + const callback = jest.fn((value, values) => { + expect(value).toBe(updated.textBoxName) + expect(values).toBe(updated) + }) + + let initialValues = initial + + const { result, rerender } = renderHook( + () => + useOnFieldChange( + 'textBoxName', + // Condition always true, just need to change a value inside the form to trigger this hook + true, + callback, + ), + { + wrapper: ({ children }) => ( + {children} + ), + }, + ) + + expect(result.current).toBeUndefined() + + expect(callback).toHaveBeenCalledTimes(0) + + initialValues = updated + + rerender() + + expect(callback).toHaveBeenCalledTimes(1) + }) + + test('should render when condition change', () => { + const callback = jest.fn() + + let initialValues = initial + + const { result, rerender } = renderHook( + ({ condition }) => { + useOnFieldChange( + 'textBoxName', + // Condition will depends of rerender({ condition: '' }) + condition, + callback, + ) + }, + { + wrapper: ({ children }) => ( + {children} + ), + + initialProps: { + condition: false, + }, + }, + ) + + expect(result.current).toBeUndefined() + + expect(callback).toHaveBeenCalledTimes(0) + + initialValues = updated + + rerender({ condition: true }) + + expect(callback).toHaveBeenCalledTimes(1) + }) +}) diff --git a/src/hooks/index.ts b/src/hooks/index.ts index d28fd5f0..a0055dc0 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1,2 +1,3 @@ export { useValidation } from './useValidation' export { useFormField } from './useFormField' +export { useOnFieldChange } from './useOnFieldChange' diff --git a/src/hooks/useOnFieldChange.ts b/src/hooks/useOnFieldChange.ts new file mode 100644 index 00000000..b1c05422 --- /dev/null +++ b/src/hooks/useOnFieldChange.ts @@ -0,0 +1,29 @@ +import { useEffect, useRef } from 'react' +import { useField, useFormState } from 'react-final-form' + +type CallbackFn = ( + value: FieldValue, + values: AllValues, +) => unknown + +export const useOnFieldChange = ( + name: string, + condition: boolean, + callback: CallbackFn, +): void => { + const { values } = useFormState() + const { + input: { value }, + } = useField(name, { + allowNull: true, + subscription: { value: true }, + }) + const previousValues = useRef(value) + + useEffect(() => { + if (previousValues.current !== value && condition) { + previousValues.current = value + callback(value, values) + } + }, [value, values, callback, condition]) +} diff --git a/src/index.ts b/src/index.ts index 39560060..2c88228f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,7 +17,7 @@ export { TimeField, ToggleField, } from './components' -export { useValidation } from './hooks' +export { useValidation, useOnFieldChange } from './hooks' export type { BaseFieldProps, FormErrors } from './types' export { pickValidators } from './helpers' export { useErrors, ErrorProvider } from './providers/ErrorContext'