1
- import React , { useState , useRef } from 'react' ;
2
- import Form from '../form' ;
3
1
import arrayMutators from 'final-form-arrays' ;
4
- import PropTypes from 'prop-types' ;
5
2
import createFocusDecorator from 'final-form-focus' ;
3
+ import PropTypes from 'prop-types' ;
4
+ import React , { useCallback , useMemo , useRef , useState } from 'react' ;
6
5
6
+ import defaultSchemaValidator from '../default-schema-validator' ;
7
+ import defaultValidatorMapper from '../validator-mapper' ;
8
+ import Form from '../form' ;
7
9
import RendererContext from '../renderer-context' ;
8
10
import renderForm from './render-form' ;
9
- import defaultSchemaValidator from '../default-schema-validator' ;
10
11
import SchemaErrorComponent from './schema-error-component' ;
11
- import defaultValidatorMapper from '../validator-mapper' ;
12
+
13
+
14
+ const isFunc = ( fn ) => ( typeof fn === 'function' ) ;
12
15
13
16
const FormRenderer = ( {
17
+ actionMapper,
14
18
children,
19
+ clearedValue,
20
+ clearOnUnmount,
15
21
componentMapper,
22
+ decorators,
16
23
FormTemplate,
17
24
FormTemplateProps,
18
- onSubmit ,
25
+ mutators ,
19
26
onCancel,
27
+ onError,
20
28
onReset,
21
- clearOnUnmount,
22
- subscription,
23
- clearedValue,
29
+ onSubmit,
24
30
schema,
25
- validatorMapper,
26
- actionMapper,
27
31
schemaValidatorMapper,
32
+ subscription,
33
+ validatorMapper,
28
34
...props
29
35
} ) => {
30
36
const [ fileInputs , setFileInputs ] = useState ( [ ] ) ;
31
37
const registeredFields = useRef ( { } ) ;
32
38
const focusDecorator = useRef ( createFocusDecorator ( ) ) ;
33
- let schemaError ;
39
+ const validatorMapperMerged = useMemo ( ( ) => ( {
40
+ ...defaultValidatorMapper ,
41
+ ...validatorMapper
42
+ } ) , [ validatorMapper ] ) ;
43
+ const mutatorsMerged = useMemo ( ( ) => ( {
44
+ ...arrayMutators ,
45
+ ...mutators
46
+ } ) , [ mutators ] ) ;
47
+ const decoratorsMerged = useMemo ( ( ) => ( [
48
+ focusDecorator . current ,
49
+ ...( Array . isArray ( decorators ) ? decorators : [ ] )
50
+ ] ) , [ decorators ] ) ;
51
+
52
+ const handleSubmitCallback = useCallback ( ( values , formApi , ...args ) => {
53
+ return ! isFunc ( onSubmit ) ? void 0 : onSubmit ( values , { ...formApi , fileInputs} , ...args ) ;
54
+ } , [ onSubmit , fileInputs ] ) ;
55
+
56
+ const handleCancelCallback = useCallback ( ( getState ) => ( ...args ) => {
57
+ return ! isFunc ( onCancel ) ? void 0 : onCancel ( getState ( ) . values , ...args ) ;
58
+ } , [ onCancel ] ) ;
59
+
60
+ const handleResetCallback = useCallback ( ( reset ) => ( ...args ) => {
61
+ reset ( ) ;
62
+ return ! isFunc ( onReset ) ? void 0 : onReset ( ...args ) ;
63
+ } , [ onReset ] ) ;
34
64
35
- const setRegisteredFields = ( fn ) => ( registeredFields . current = fn ( { ...registeredFields . current } ) ) ;
36
- const internalRegisterField = ( name ) => {
37
- setRegisteredFields ( ( prev ) => ( prev [ name ] ? { ...prev , [ name ] : prev [ name ] + 1 } : { ...prev , [ name ] : 1 } ) ) ;
38
- } ;
65
+ const handleErrorCallback = useCallback ( ( ...args ) => {
66
+ // eslint-disable-next-line no-console
67
+ console . error ( ...args ) ;
68
+ return ! isFunc ( onError ) ? void 0 : onError ( ...args ) ;
69
+ } , [ onError ] ) ;
70
+
71
+ const registerInputFile = useCallback ( ( name ) => {
72
+ setFileInputs ( ( prevFiles ) => [ ...prevFiles , name ] ) ;
73
+ } , [ ] ) ;
74
+
75
+ const unRegisterInputFile = useCallback ( ( name ) => {
76
+ setFileInputs ( ( prevFiles ) => [
77
+ ...prevFiles . splice ( prevFiles . indexOf ( name ) )
78
+ ] ) ;
79
+ } , [ ] ) ;
39
80
40
- const internalUnRegisterField = ( name ) => {
41
- setRegisteredFields ( ( { [ name ] : currentField , ... prev } ) => ( currentField && currentField > 1 ? { [ name ] : currentField - 1 , ...prev } : prev ) ) ;
42
- } ;
81
+ const setRegisteredFields = useCallback ( ( fn ) => {
82
+ return registeredFields . current = fn ( { ...registeredFields . current } ) ;
83
+ } , [ ] ) ;
43
84
44
- const internalGetRegisteredFields = ( ) =>
45
- Object . entries ( registeredFields . current ) . reduce ( ( acc , [ name , value ] ) => ( value > 0 ? [ ...acc , name ] : acc ) , [ ] ) ;
85
+ const internalRegisterField = useCallback ( ( name ) => {
86
+ setRegisteredFields ( ( prev ) => (
87
+ prev [ name ] ? { ...prev , [ name ] : prev [ name ] + 1 } : { ...prev , [ name ] : 1 } )
88
+ ) ;
89
+ } , [ setRegisteredFields ] ) ;
46
90
47
- const validatorMapperMerged = { ...defaultValidatorMapper , ...validatorMapper } ;
91
+ const internalUnRegisterField = useCallback ( ( name ) => {
92
+ setRegisteredFields ( ( { [ name ] : currentField , ...prev } ) => (
93
+ currentField && currentField > 1 ? { [ name ] : currentField - 1 , ...prev } : prev
94
+ ) ) ;
95
+ } , [ setRegisteredFields ] ) ;
96
+
97
+ const internalGetRegisteredFields = useCallback ( ( ) => {
98
+ const fields = registeredFields . current ;
99
+ Object . entries ( fields ) . reduce ( ( acc , [ name , value ] ) => (
100
+ value > 0 ? [ ...acc , name ] : acc
101
+ ) , [ ] ) ;
102
+ } , [ ] ) ;
48
103
49
104
try {
50
105
const validatorTypes = Object . keys ( validatorMapperMerged ) ;
51
106
const actionTypes = actionMapper ? Object . keys ( actionMapper ) : [ ] ;
52
- defaultSchemaValidator ( schema , componentMapper , validatorTypes , actionTypes , schemaValidatorMapper ) ;
53
- } catch ( error ) {
54
- schemaError = error ;
55
- // eslint-disable-next-line no-console
56
- console . error ( error ) ;
57
- // eslint-disable-next-line no-console
58
- console . log ( 'error: ' , error . message ) ;
59
- }
60
107
61
- if ( schemaError ) {
62
- return < SchemaErrorComponent name = { schemaError . name } message = { schemaError . message } /> ;
108
+ defaultSchemaValidator (
109
+ schema ,
110
+ componentMapper ,
111
+ validatorTypes ,
112
+ actionTypes ,
113
+ schemaValidatorMapper
114
+ ) ;
115
+ }
116
+ catch ( error ) {
117
+ handleErrorCallback ( 'schema-error' , error ) ;
118
+ return < SchemaErrorComponent name = { error . name } message = { error . message } /> ;
63
119
}
64
120
65
- const registerInputFile = ( name ) => setFileInputs ( ( prevFiles ) => [ ...prevFiles , name ] ) ;
66
-
67
- const unRegisterInputFile = ( name ) => setFileInputs ( ( prevFiles ) => [ ...prevFiles . splice ( prevFiles . indexOf ( name ) ) ] ) ;
121
+ const formFields = useMemo ( ( ) => renderForm ( schema . fields ) , [ schema ] ) ;
68
122
69
123
return (
70
124
< Form
71
- { ...props }
72
- onSubmit = { ( values , formApi , ...args ) => onSubmit ( values , { ...formApi , fileInputs } , ...args ) }
73
- mutators = { { ...arrayMutators } }
74
- decorators = { [ focusDecorator . current ] }
75
- subscription = { { pristine : true , submitting : true , valid : true , ...subscription } }
76
- render = { ( { handleSubmit, pristine, valid, form : { reset, mutators, getState, submit, ...form } } ) => (
125
+ onSubmit = { handleSubmitCallback }
126
+ mutators = { mutatorsMerged }
127
+ decorators = { decoratorsMerged }
128
+ subscription = { { pristine : true , submitting : true , valid : true , ...subscription } }
129
+ render = { ( { handleSubmit, pristine, valid, form : { reset, mutators, getState, submit, ...form } } ) => (
77
130
< RendererContext . Provider
78
131
value = { {
79
132
componentMapper,
@@ -84,11 +137,9 @@ const FormRenderer = ({
84
137
unRegisterInputFile,
85
138
pristine,
86
139
onSubmit,
87
- onCancel : onCancel ? ( ...args ) => onCancel ( getState ( ) . values , ...args ) : undefined ,
88
- onReset : ( ...args ) => {
89
- onReset && onReset ( ...args ) ;
90
- reset ( ) ;
91
- } ,
140
+ onCancel : handleCancelCallback ( getState ) ,
141
+ onReset : handleResetCallback ( reset ) ,
142
+ onError : handleErrorCallback ,
92
143
getState,
93
144
valid,
94
145
clearedValue,
@@ -107,33 +158,41 @@ const FormRenderer = ({
107
158
} ,
108
159
} }
109
160
>
110
- { typeof children === 'function' ? children ( { schema, formFields : renderForm ( schema . fields ) } ) : (
111
- < React . Fragment >
112
- < FormTemplate
113
- formFields = { renderForm ( schema . fields ) }
114
- schema = { schema }
115
- { ...FormTemplateProps }
116
- />
117
- { children }
118
- </ React . Fragment >
161
+
162
+ { FormTemplate && (
163
+ < FormTemplate
164
+ formFields = { formFields }
165
+ schema = { schema }
166
+ { ...FormTemplateProps }
167
+ />
119
168
) }
169
+
170
+ { isFunc ( children ) ? children ( { formFields, schema} ) : children }
171
+
120
172
</ RendererContext . Provider >
121
173
) }
174
+ { ...props }
122
175
/>
123
176
) ;
124
177
} ;
125
178
126
179
FormRenderer . propTypes = {
127
180
children : PropTypes . oneOfType ( [ PropTypes . func , PropTypes . element ] ) ,
128
- onSubmit : PropTypes . func . isRequired ,
181
+ onSubmit : PropTypes . func ,
129
182
onCancel : PropTypes . func ,
130
183
onReset : PropTypes . func ,
184
+ onError : PropTypes . func ,
131
185
schema : PropTypes . object . isRequired ,
132
186
clearOnUnmount : PropTypes . bool ,
133
- subscription : PropTypes . shape ( { [ PropTypes . string ] : PropTypes . bool } ) ,
187
+ subscription : PropTypes . shape ( { [ PropTypes . string ] : PropTypes . bool } ) ,
134
188
clearedValue : PropTypes . any ,
135
189
componentMapper : PropTypes . shape ( {
136
- [ PropTypes . string ] : PropTypes . oneOfType ( [ PropTypes . node , PropTypes . element , PropTypes . func , PropTypes . elementType ] ) ,
190
+ [ PropTypes . string ] : PropTypes . oneOfType ( [
191
+ PropTypes . node ,
192
+ PropTypes . element ,
193
+ PropTypes . func ,
194
+ PropTypes . elementType
195
+ ] ) ,
137
196
} ) . isRequired ,
138
197
FormTemplate : PropTypes . elementType ,
139
198
FormTemplateProps : PropTypes . object ,
0 commit comments