Skip to content

Commit

Permalink
Merge pull request #93 from redhat-developer/backportjanus2573
Browse files Browse the repository at this point in the history
fix(orchestrator): resolve rerender form decorator on every change
  • Loading branch information
mareklibra authored Nov 28, 2024
2 parents dacc7ff + c301dbd commit fd260b4
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 21 deletions.
5 changes: 5 additions & 0 deletions workspaces/orchestrator/.changeset/shiny-mangos-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@red-hat-developer-hub/backstage-plugin-orchestrator-form-react': patch
---

resolve rerendering form decorator on every change
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React, { Fragment } from 'react';

import { JsonObject } from '@backstage/types';
Expand All @@ -39,15 +40,28 @@ const getNumSteps = (schema: JSONSchema7): number | undefined => {

const SingleStepForm = ({
schema,
formData,
onChange,
initialFormData,
onSubmit,
uiSchema,
}: {
schema: JSONSchema7;
formData: JsonObject;
onChange: (formData: JsonObject) => void;
initialFormData?: JsonObject;
onSubmit: (formData: JsonObject) => void;
uiSchema: UiSchema<JsonObject>;
}) => {
const [_initialFormData, setInitialFormData] = React.useState<
JsonObject | undefined
>(initialFormData);

const _onSubmit = React.useCallback(
(formData: JsonObject) => {
// Since the review step is outside of the MuiForm component in SingleStepForm, we need to load the current values when navigating back.
setInitialFormData(formData);
onSubmit(formData);
},
[onSubmit, setInitialFormData],
);

const steps = React.useMemo<OrchestratorFormStep[]>(() => {
return [
{
Expand All @@ -56,16 +70,16 @@ const SingleStepForm = ({
content: (
<OrchestratorFormWrapper
schema={{ ...schema, title: '' }}
formData={formData}
onChange={onChange}
initialFormData={_initialFormData}
onSubmit={_onSubmit}
uiSchema={uiSchema}
>
<OrchestratorFormToolbar />
</OrchestratorFormWrapper>
),
},
];
}, [schema, formData, onChange, uiSchema]);
}, [schema, _initialFormData, uiSchema, _onSubmit]);
return <OrchestratorFormStepper steps={steps} />;
};

Expand Down Expand Up @@ -103,7 +117,7 @@ const OrchestratorForm = ({
handleExecute(formData || {});
}, [formData, handleExecute]);

const onChange = React.useCallback(
const onSubmit = React.useCallback(
(_formData: JsonObject) => {
setFormData(_formData);
},
Expand Down Expand Up @@ -136,17 +150,17 @@ const OrchestratorForm = ({
<OrchestratorFormWrapper
schema={schema}
numStepsInMultiStepSchema={numStepsInMultiStepSchema}
formData={formData}
onChange={onChange}
onSubmit={onSubmit}
uiSchema={uiSchema}
initialFormData={data}
>
<Fragment />
</OrchestratorFormWrapper> // it is required to pass the fragment so rjsf won't generate a Submit button
) : (
<SingleStepForm
schema={schema}
onChange={onChange}
formData={formData}
onSubmit={onSubmit}
initialFormData={data}
uiSchema={uiSchema}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React from 'react';

import { ErrorPanel } from '@backstage/core-components';
Expand Down Expand Up @@ -42,9 +43,9 @@ type OrchestratorFormWrapperProps = {
schema: JSONSchema7;
numStepsInMultiStepSchema?: number;
children: React.ReactNode;
formData: JsonObject;
onChange: (formData: JsonObject) => void;
onSubmit: (formData: JsonObject) => void;
uiSchema: UiSchema<JsonObject, JSONSchema7>;
initialFormData?: JsonObject;
};

const WrapperFormPropsContext =
Expand All @@ -64,13 +65,17 @@ const FormComponent = (decoratorProps: FormDecoratorProps) => {
numStepsInMultiStepSchema,
uiSchema,
schema,
onChange,
formData,
onSubmit: _onSubmit,
initialFormData,
children,
} = props;
const [extraErrors, setExtraErrors] = React.useState<
ErrorSchema<JsonObject> | undefined
>();
// make this form a controlled component so state will remain when moving between steps. see https://rjsf-team.github.io/react-jsonschema-form/docs/quickstart#controlled-component
const [formData, setFormData] = React.useState<JsonObject>(
initialFormData || {},
);
const isMultiStep = numStepsInMultiStepSchema !== undefined;
const { handleNext, activeStep, handleValidateStarted, handleValidateEnded } =
useStepperContext();
Expand Down Expand Up @@ -111,6 +116,7 @@ const FormComponent = (decoratorProps: FormDecoratorProps) => {
!_validationError &&
activeStep < (numStepsInMultiStepSchema || 1)
) {
_onSubmit(_formData);
handleNext();
}
};
Expand All @@ -134,7 +140,7 @@ const FormComponent = (decoratorProps: FormDecoratorProps) => {
extraErrors={extraErrors}
onSubmit={e => onSubmit(e.formData || {})}
onChange={e => {
onChange(e.formData || {});
setFormData(e.formData || {});
if (decoratorProps.onChange) {
decoratorProps.onChange(e);
}
Expand All @@ -150,18 +156,22 @@ const FormComponent = (decoratorProps: FormDecoratorProps) => {
const OrchestratorFormWrapper = ({
schema,
uiSchema,
formData,
initialFormData,
...props
}: OrchestratorFormWrapperProps) => {
const formApi =
useApiHolder().get(orchestratorFormApiRef) || defaultFormExtensionsApi;
const NewComponent = React.useMemo(() => {
const formDecorator = formApi.getFormDecorator(schema, uiSchema, formData);
const formDecorator = formApi.getFormDecorator(
schema,
uiSchema,
initialFormData,
);
return formDecorator(FormComponent);
}, [schema, formApi, uiSchema, formData]);
}, [schema, uiSchema, formApi, initialFormData]);
return (
<WrapperFormPropsContext.Provider
value={{ schema, uiSchema, formData, ...props }}
value={{ schema, uiSchema, initialFormData, ...props }}
>
<NewComponent />
</WrapperFormPropsContext.Provider>
Expand Down

0 comments on commit fd260b4

Please sign in to comment.