Skip to content

Commit 611ba1e

Browse files
authored
Merge pull request #26 from vtex/fix/first-render-change
Prevents onChange trigger on first render
2 parents 82fe578 + 7873dfa commit 611ba1e

File tree

5 files changed

+106
-2
lines changed

5 files changed

+106
-2
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.2.0-beta.13] - 2020-03-26
11+
12+
### Fixed
13+
14+
- Prevent `onChange` from being triggered during the initial render.
15+
1016
## [0.2.0-beta.12] - 2020-03-20
1117

1218
### Added

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-hook-form-jsonschema",
3-
"version": "0.2.0-beta.12",
3+
"version": "0.2.0-beta.13",
44
"description": "Wrapper arround react-hook-form to create forms from a JSON schema.",
55
"main": "output/index.cjs.js",
66
"module": "output/index.esm.js",

src/components/FormContext.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,14 @@ export const FormContext: FC<FormContextProps> = props => {
3535
submitFocusError: submitFocusError,
3636
})
3737

38+
const isFirstRender = React.useRef(true)
39+
3840
if (typeof onChange === 'function') {
39-
onChange(getObjectFromForm(props.schema, methods.watch()))
41+
const watchedInputs = methods.watch()
42+
43+
if (isFirstRender.current === false) {
44+
onChange(getObjectFromForm(props.schema, watchedInputs))
45+
}
4046
}
4147

4248
const idMap = useMemo(() => getIdSchemaPairs(props.schema), [props.schema])
@@ -72,6 +78,10 @@ export const FormContext: FC<FormContextProps> = props => {
7278
formProps.noValidate = props.noNativeValidate
7379
}
7480

81+
if (isFirstRender.current === true) {
82+
isFirstRender.current = false
83+
}
84+
7585
return (
7686
<InternalFormContext.Provider value={formContext}>
7787
<form {...formProps}>{props.children}</form>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const mockSchema = {
2+
$id: 'https://example.com/mock.schema.json',
3+
$schema: 'http://json-schema.org/draft-07/schema#',
4+
title: 'Mock',
5+
type: 'object',
6+
properties: {
7+
firstField: {
8+
type: 'string',
9+
description: 'The first field.',
10+
title: 'First field',
11+
},
12+
secondField: {
13+
type: 'string',
14+
description: 'The second field.',
15+
title: 'Second field',
16+
},
17+
thirdField: {
18+
type: 'string',
19+
description: 'The third field.',
20+
title: 'Third field',
21+
},
22+
},
23+
}
24+
25+
export default mockSchema
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { fireEvent, render } from '@vtex/test-tools/react'
2+
import React from 'react'
3+
import { Controller } from 'react-hook-form'
4+
5+
import { useObject } from '../../hooks/useObject'
6+
import mockSchema from '../__mocks__/mockSchema'
7+
import { FormContext } from '../FormContext'
8+
9+
const ObjectRenderer = (props: { pointer: string }) => {
10+
const fields = useObject({ pointer: props.pointer })
11+
12+
return (
13+
<>
14+
{fields.map(field => {
15+
const fieldJsonSchema = field.getObject()
16+
17+
return (
18+
<Controller
19+
as={<input aria-label={fieldJsonSchema.title} name={field.name} />}
20+
control={field.formContext.control}
21+
defaultValue=""
22+
key={field.pointer}
23+
name={field.pointer}
24+
/>
25+
)
26+
})}
27+
</>
28+
)
29+
}
30+
31+
test('should call onChange when something changes', () => {
32+
const changeHandlerMock = jest.fn()
33+
34+
const { getByLabelText } = render(
35+
<FormContext onChange={changeHandlerMock} schema={mockSchema}>
36+
<ObjectRenderer pointer="#" />
37+
</FormContext>
38+
)
39+
40+
expect(changeHandlerMock).toHaveBeenCalledTimes(0)
41+
42+
let changeValue
43+
44+
Object.entries(mockSchema.properties).forEach(
45+
([fieldName, fieldProperties], index) => {
46+
const fieldNumber = index + 1
47+
48+
const inputElement = getByLabelText(fieldProperties.title)
49+
50+
const fieldNewValue = `new value for field ${fieldNumber}`
51+
52+
fireEvent.change(inputElement, {
53+
target: { value: fieldNewValue },
54+
})
55+
56+
expect(changeHandlerMock).toHaveBeenCalledTimes(fieldNumber)
57+
58+
changeValue = { ...changeValue, [fieldName]: fieldNewValue }
59+
60+
expect(changeHandlerMock).toHaveBeenLastCalledWith(changeValue)
61+
}
62+
)
63+
})

0 commit comments

Comments
 (0)