Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: #3924 by preventing an undefined schema for fixed array items #3929

Merged
merged 1 commit into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@ should change the heading of the (upcoming) version to include a major version b
-->
# 5.13.3

## @rjsf/utils
## @rjsf/antd

- Updated `toPathSchemaInternal()` util to generate correct path schemas for fixed arrays by picking up individual schemas in the `items` array, fixing [#3909](https://github.com/rjsf-team/react-jsonschema-form/issues/3909)
- Fixed the `SelectWidget` so that filtering works by reworking how `options` are passed to the underlying `Select`

## @rjsf/core

- Replaced the deprecated `UNSAFE_componentWillReceiveProps()` method in the Form.tsx component with an improved solution utilizing the React lifecycle methods: `getSnapshotBeforeUpdate()` and `componentDidUpdate()`. Fixing [#1794](https://github.com/rjsf-team/react-jsonschema-form/issues/1794)
- Fixed the `ArrayField` implementation to never pass an undefined schema for fixed arrays to other methods, fixing [#3924](https://github.com/rjsf-team/react-jsonschema-form/issues/3924)
- Fixed a refresh issue in `getSnapshotBeforeUpdate()` and `componentDidUpdate()` caused by the fix for #1794, fixing [#3927](https://github.com/rjsf-team/react-jsonschema-form/issues/3927)

## @rjsf/utils

- Updated `toPathSchemaInternal()` util to generate correct path schemas for fixed arrays by picking up individual schemas in the `items` array, fixing [#3909](https://github.com/rjsf-team/react-jsonschema-form/issues/3909)

# 5.13.2

Expand Down
36 changes: 23 additions & 13 deletions packages/core/src/components/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -281,35 +281,45 @@ export default class Form<
}

/**
* `getSnapshotBeforeUpdate` is a React lifecycle method that is invoked right before the most recently rendered output is committed to the DOM.
* It enables your component to capture current values (e.g., scroll position) before they are potentially changed.
* `getSnapshotBeforeUpdate` is a React lifecycle method that is invoked right before the most recently rendered
* output is committed to the DOM. It enables your component to capture current values (e.g., scroll position) before
* they are potentially changed.
*
* In this case, it checks if the `formData` prop has changed since the last render. If it has, it computes the next state of the component
* using `getStateFromProps` method and returns it along with a `shouldUpdate` flag set to `true`. This ensures that we have the most up-to-date
* In this case, it checks if the props have changed since the last render. If they have, it computes the next state
* of the component using `getStateFromProps` method and returns it along with a `shouldUpdate` flag set to `true` IF
* the `nextState` and `prevState` are different, otherwise `false`. This ensures that we have the most up-to-date
* state ready to be applied in `componentDidUpdate`.
*
* If `formData` hasn't changed, it simply returns an object with `shouldUpdate` set to `false`, indicating that a state update is not necessary.
* If `formData` hasn't changed, it simply returns an object with `shouldUpdate` set to `false`, indicating that a
* state update is not necessary.
*
* @param prevProps - The previous set of props before the update.
* @returns Either an object containing the next state and a flag indicating that an update should occur, or an object with a flag indicating that an update is not necessary.
* @param prevState - The previous state before the update.
* @returns Either an object containing the next state and a flag indicating that an update should occur, or an object
* with a flag indicating that an update is not necessary.
*/
getSnapshotBeforeUpdate(
prevProps: FormProps<T, S, F>
prevProps: FormProps<T, S, F>,
prevState: FormState<T, S, F>
): { nextState: FormState<T, S, F>; shouldUpdate: true } | { shouldUpdate: false } {
if (!deepEquals(this.props.formData, prevProps.formData)) {
if (!deepEquals(this.props, prevProps)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for finding and fixing this

const nextState = this.getStateFromProps(this.props, this.props.formData);
return { nextState, shouldUpdate: true };
const shouldUpdate = !deepEquals(nextState, prevState);
return { nextState, shouldUpdate };
}
return { shouldUpdate: false };
}

/**
* `componentDidUpdate` is a React lifecycle method that is invoked immediately after updating occurs. This method is not called for the initial render.
* `componentDidUpdate` is a React lifecycle method that is invoked immediately after updating occurs. This method is
* not called for the initial render.
*
* Here, it checks if an update is necessary based on the `shouldUpdate` flag received from `getSnapshotBeforeUpdate`. If an update is required,
* it applies the next state and, if needed, triggers the `onChange` handler to inform about changes.
* Here, it checks if an update is necessary based on the `shouldUpdate` flag received from `getSnapshotBeforeUpdate`.
* If an update is required, it applies the next state and, if needed, triggers the `onChange` handler to inform about
* changes.
*
* This method effectively replaces the deprecated `UNSAFE_componentWillReceiveProps`, providing a safer alternative to handle prop changes and state updates.
* This method effectively replaces the deprecated `UNSAFE_componentWillReceiveProps`, providing a safer alternative
* to handle prop changes and state updates.
*
* @param _ - The previous set of props.
* @param prevState - The previous state of the component before the update.
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/components/fields/ArrayField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -743,9 +743,9 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
const itemCast = item as unknown as T[];
const additional = index >= itemSchemas.length;
const itemSchema =
additional && isObject(schema.additionalItems)
(additional && isObject(schema.additionalItems)
? schemaUtils.retrieveSchema(schema.additionalItems as S, itemCast)
: itemSchemas[index];
: itemSchemas[index]) || {};
const itemIdPrefix = idSchema.$id + idSeparator + index;
const itemIdSchema = schemaUtils.toIdSchema(itemSchema, itemIdPrefix, itemCast, idPrefix, idSeparator);
const itemUiSchema = additional
Expand Down
23 changes: 23 additions & 0 deletions packages/core/test/ArrayField.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1985,6 +1985,29 @@ describe('ArrayField', () => {
expect(node.querySelectorAll('textarea').length).to.eql(2);
});

it('[fixed] should silently handle additional formData not covered by fixed array', () => {
const { node, onSubmit } = createFormComponent({
schema: {
type: 'array',
items: [
{
type: 'string',
},
{
type: 'string',
},
],
},
formData: ['foo', 'bar', 'baz'],
});
expect(node.querySelectorAll('input').length).to.eql(2);
submitForm(node);

sinon.assert.calledWithMatch(onSubmit.lastCall, {
formData: ['foo', 'bar', 'baz'],
});
});

describe('operations for additional items', () => {
const { node, onChange } = createFormComponent({
schema: schemaAdditional,
Expand Down