Skip to content
This repository has been archived by the owner on Dec 27, 2022. It is now read-only.

feat(hook): add useOnFieldChange #575

Merged
merged 1 commit into from
Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions src/hooks/__tests__/useOnFieldChange.spec.tsx
Original file line number Diff line number Diff line change
@@ -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) => (
<ThemeProvider theme={lightTheme}>
<Form<FormValues>
initialValues={initialValues}
errors={mockErrors}
onRawSubmit={() => {}}
>
{children}
<CheckboxField name="check" />
<TextBoxField name="textBoxName" type="text" />
</Form>
</ThemeProvider>
)

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<FormValues['textBoxName'], FormValues>(
'textBoxName',
// Condition always true, just need to change a value inside the form to trigger this hook
true,
callback,
),
{
wrapper: ({ children }) => (
<Wrapper initialValues={initialValues}>{children}</Wrapper>
),
},
)

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<FormValues['textBoxName'], FormValues>(
'textBoxName',
// Condition will depends of rerender({ condition: '' })
condition,
callback,
)
},
{
wrapper: ({ children }) => (
<Wrapper initialValues={initialValues}>{children}</Wrapper>
),

initialProps: {
condition: false,
},
},
)

expect(result.current).toBeUndefined()

expect(callback).toHaveBeenCalledTimes(0)

initialValues = updated

rerender({ condition: true })

expect(callback).toHaveBeenCalledTimes(1)
})
})
1 change: 1 addition & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { useValidation } from './useValidation'
export { useFormField } from './useFormField'
export { useOnFieldChange } from './useOnFieldChange'
29 changes: 29 additions & 0 deletions src/hooks/useOnFieldChange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useEffect, useRef } from 'react'
import { useField, useFormState } from 'react-final-form'

type CallbackFn<FieldValue, AllValues> = (
value: FieldValue,
values: AllValues,
) => unknown

export const useOnFieldChange = <FieldValue = unknown, AllValues = unknown>(
name: string,
condition: boolean,
callback: CallbackFn<FieldValue, AllValues>,
): void => {
const { values } = useFormState<AllValues>()
const {
input: { value },
} = useField<FieldValue>(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])
}
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'