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