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

Commit e5826fb

Browse files
authored
feat(hook): add useOnFieldChange (#575)
1 parent ec696b9 commit e5826fb

File tree

4 files changed

+144
-1
lines changed

4 files changed

+144
-1
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { ThemeProvider } from '@emotion/react'
2+
import { theme as lightTheme } from '@scaleway/ui'
3+
import { renderHook } from '@testing-library/react'
4+
import type { ReactElement } from 'react'
5+
import { CheckboxField, Form, TextBoxField } from '../../components'
6+
import { mockErrors } from '../../mocks'
7+
import { useOnFieldChange } from '../useOnFieldChange'
8+
9+
type FormValues = {
10+
textBoxName: string
11+
check: boolean
12+
}
13+
14+
type Wrapers = {
15+
children: ReactElement
16+
initialValues: FormValues
17+
}
18+
19+
const initial = {
20+
textBoxName: 'test',
21+
check: true,
22+
}
23+
24+
const updated = {
25+
textBoxName: 'updated',
26+
check: false,
27+
}
28+
29+
const Wrapper = ({ children, initialValues }: Wrapers) => (
30+
<ThemeProvider theme={lightTheme}>
31+
<Form<FormValues>
32+
initialValues={initialValues}
33+
errors={mockErrors}
34+
onRawSubmit={() => {}}
35+
>
36+
{children}
37+
<CheckboxField name="check" />
38+
<TextBoxField name="textBoxName" type="text" />
39+
</Form>
40+
</ThemeProvider>
41+
)
42+
43+
describe('useOnFieldChange', () => {
44+
test('should render correctly', () => {
45+
const callback = jest.fn((value, values) => {
46+
expect(value).toBe(updated.textBoxName)
47+
expect(values).toBe(updated)
48+
})
49+
50+
let initialValues = initial
51+
52+
const { result, rerender } = renderHook(
53+
() =>
54+
useOnFieldChange<FormValues['textBoxName'], FormValues>(
55+
'textBoxName',
56+
// Condition always true, just need to change a value inside the form to trigger this hook
57+
true,
58+
callback,
59+
),
60+
{
61+
wrapper: ({ children }) => (
62+
<Wrapper initialValues={initialValues}>{children}</Wrapper>
63+
),
64+
},
65+
)
66+
67+
expect(result.current).toBeUndefined()
68+
69+
expect(callback).toHaveBeenCalledTimes(0)
70+
71+
initialValues = updated
72+
73+
rerender()
74+
75+
expect(callback).toHaveBeenCalledTimes(1)
76+
})
77+
78+
test('should render when condition change', () => {
79+
const callback = jest.fn()
80+
81+
let initialValues = initial
82+
83+
const { result, rerender } = renderHook(
84+
({ condition }) => {
85+
useOnFieldChange<FormValues['textBoxName'], FormValues>(
86+
'textBoxName',
87+
// Condition will depends of rerender({ condition: '' })
88+
condition,
89+
callback,
90+
)
91+
},
92+
{
93+
wrapper: ({ children }) => (
94+
<Wrapper initialValues={initialValues}>{children}</Wrapper>
95+
),
96+
97+
initialProps: {
98+
condition: false,
99+
},
100+
},
101+
)
102+
103+
expect(result.current).toBeUndefined()
104+
105+
expect(callback).toHaveBeenCalledTimes(0)
106+
107+
initialValues = updated
108+
109+
rerender({ condition: true })
110+
111+
expect(callback).toHaveBeenCalledTimes(1)
112+
})
113+
})

src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { useValidation } from './useValidation'
22
export { useFormField } from './useFormField'
3+
export { useOnFieldChange } from './useOnFieldChange'

src/hooks/useOnFieldChange.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useEffect, useRef } from 'react'
2+
import { useField, useFormState } from 'react-final-form'
3+
4+
type CallbackFn<FieldValue, AllValues> = (
5+
value: FieldValue,
6+
values: AllValues,
7+
) => unknown
8+
9+
export const useOnFieldChange = <FieldValue = unknown, AllValues = unknown>(
10+
name: string,
11+
condition: boolean,
12+
callback: CallbackFn<FieldValue, AllValues>,
13+
): void => {
14+
const { values } = useFormState<AllValues>()
15+
const {
16+
input: { value },
17+
} = useField<FieldValue>(name, {
18+
allowNull: true,
19+
subscription: { value: true },
20+
})
21+
const previousValues = useRef(value)
22+
23+
useEffect(() => {
24+
if (previousValues.current !== value && condition) {
25+
previousValues.current = value
26+
callback(value, values)
27+
}
28+
}, [value, values, callback, condition])
29+
}

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export {
1717
TimeField,
1818
ToggleField,
1919
} from './components'
20-
export { useValidation } from './hooks'
20+
export { useValidation, useOnFieldChange } from './hooks'
2121
export type { BaseFieldProps, FormErrors } from './types'
2222
export { pickValidators } from './helpers'
2323
export { useErrors, ErrorProvider } from './providers/ErrorContext'

0 commit comments

Comments
 (0)