1- import React , { useState , useRef } from 'react' ;
2- import Form from '../form' ;
31import arrayMutators from 'final-form-arrays' ;
4- import PropTypes from 'prop-types' ;
52import createFocusDecorator from 'final-form-focus' ;
3+ import PropTypes from 'prop-types' ;
4+ import React , { useCallback , useMemo , useRef , useState , cloneElement } from 'react' ;
65
6+ import defaultSchemaValidator from '../default-schema-validator' ;
7+ import defaultValidatorMapper from '../validator-mapper' ;
8+ import Form from '../form' ;
79import RendererContext from '../renderer-context' ;
810import renderForm from './render-form' ;
9- import defaultSchemaValidator from '../default-schema-validator' ;
1011import SchemaErrorComponent from './schema-error-component' ;
11- import defaultValidatorMapper from '../validator-mapper' ;
12+
13+ const isFunc = ( fn ) => typeof fn === 'function' ;
1214
1315const FormRenderer = ( {
16+ actionMapper,
17+ children,
18+ clearedValue,
19+ clearOnUnmount,
1420 componentMapper,
21+ decorators,
1522 FormTemplate,
16- onSubmit,
23+ FormTemplateProps,
24+ mutators,
1725 onCancel,
26+ onError,
1827 onReset,
19- clearOnUnmount,
20- subscription,
21- clearedValue,
28+ onSubmit,
2229 schema,
23- validatorMapper,
24- actionMapper,
2530 schemaValidatorMapper,
31+ subscription,
32+ validatorMapper,
2633 ...props
2734} ) => {
2835 const [ fileInputs , setFileInputs ] = useState ( [ ] ) ;
36+ const formFields = useMemo ( ( ) => renderForm ( schema . fields ) , [ schema ] ) ;
2937 const registeredFields = useRef ( { } ) ;
3038 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 ] ) ;
3244
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 ) => {
3590 setRegisteredFields ( ( prev ) => ( prev [ name ] ? { ...prev , [ name ] : prev [ name ] + 1 } : { ...prev , [ name ] : 1 } ) ) ;
36- } ;
91+ } , [ ] ) ;
3792
38- const internalUnRegisterField = ( name ) => {
93+ const internalUnRegisterField = useCallback ( ( name ) => {
3994 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+ } , [ ] ) ;
4496
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+ } , [ ] ) ;
46101
47102 try {
48103 const validatorTypes = Object . keys ( validatorMapperMerged ) ;
49104 const actionTypes = actionMapper ? Object . keys ( actionMapper ) : [ ] ;
105+
50106 defaultSchemaValidator ( schema , componentMapper , validatorTypes , actionTypes , schemaValidatorMapper ) ;
51107 } 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 } /> ;
61110 }
62111
63- const registerInputFile = ( name ) => setFileInputs ( ( prevFiles ) => [ ...prevFiles , name ] ) ;
64-
65- const unRegisterInputFile = ( name ) => setFileInputs ( ( prevFiles ) => [ ...prevFiles . splice ( prevFiles . indexOf ( name ) ) ] ) ;
66-
67112 return (
68113 < 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 }
73117 subscription = { { pristine : true , submitting : true , valid : true , ...subscription } }
74118 render = { ( { handleSubmit, pristine, valid, form : { reset, mutators, getState, submit, ...form } } ) => (
75119 < RendererContext . Provider
@@ -82,11 +126,9 @@ const FormRenderer = ({
82126 unRegisterInputFile,
83127 pristine,
84128 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 ,
90132 getState,
91133 valid,
92134 clearedValue,
@@ -105,25 +147,32 @@ const FormRenderer = ({
105147 } ,
106148 } }
107149 >
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 } ) }
109154 </ RendererContext . Provider >
110155 ) }
156+ { ...props }
111157 />
112158 ) ;
113159} ;
114160
115161FormRenderer . propTypes = {
116- onSubmit : PropTypes . func . isRequired ,
162+ children : PropTypes . oneOfType ( [ PropTypes . func , PropTypes . element ] ) ,
163+ onSubmit : PropTypes . func ,
117164 onCancel : PropTypes . func ,
118165 onReset : PropTypes . func ,
166+ onError : PropTypes . func ,
119167 schema : PropTypes . object . isRequired ,
120168 clearOnUnmount : PropTypes . bool ,
121169 subscription : PropTypes . shape ( { [ PropTypes . string ] : PropTypes . bool } ) ,
122170 clearedValue : PropTypes . any ,
123171 componentMapper : PropTypes . shape ( {
124172 [ PropTypes . string ] : PropTypes . oneOfType ( [ PropTypes . node , PropTypes . element , PropTypes . func , PropTypes . elementType ] ) ,
125173 } ) . isRequired ,
126- FormTemplate : PropTypes . elementType . isRequired ,
174+ FormTemplate : PropTypes . elementType ,
175+ FormTemplateProps : PropTypes . object ,
127176 validatorMapper : PropTypes . shape ( {
128177 [ PropTypes . string ] : PropTypes . func ,
129178 } ) ,
@@ -142,6 +191,8 @@ FormRenderer.propTypes = {
142191 } ) ,
143192 } ) ,
144193 initialValues : PropTypes . object ,
194+ decorators : PropTypes . array ,
195+ mutators : PropTypes . object ,
145196} ;
146197
147198FormRenderer . defaultProps = {
0 commit comments