Skip to content
Open
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
15 changes: 15 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ If you apply custom styling that relies on the previous `Grid`-based DOM structu
Furthermore, if you use exported method `renderLayoutElements`, it no longer wraps children in `Grid` items for direction `column`.
This should not affect you except if you explicitly use this method in custom renderers.

### Vue keeps the state of a form shallow

`JsonForms` of `@jsonforms/vue` now keeps its state shallow.
Vue no longer makes a proxy for each node of the `schema`, the `uischema`, the `renderers`, the `cells` and the form data, which makes a large form much faster to mount.

JSON Forms gives the value of a prop to the state as it is.
An application that keeps such a value out of deep reactive state thus no longer needs `markRaw` for it, and Vue no longer gives the "received a Component that was made a reactive object" warning for a renderer set.
An application that keeps such a value in `reactive()` state gives a proxy of the value to the form.
Use `shallowRef`, `shallowReactive` or `markRaw` for a value that must stay unwrapped, such as a renderer set, for which Vue gives the warning above.
The one exception is `ajv`, which the form always unwraps, because Ajv cannot compile a schema through a proxy of itself.

The form data that a renderer gets is no longer a proxy.
A change of the data thus needs a `dispatch` of an `update` action, which the `useJsonFormsControl` composition and its relatives do for you through `handleChange`.
A custom renderer that writes into the data object directly, for example with a `v-model` on a nested field, must use `handleChange` instead.

## Migrating to JSON Forms 3.8

### `Translator` type changed from overloaded signatures to a generic conditional type
Expand Down
4 changes: 1 addition & 3 deletions packages/vue-vuetify/dev/validate/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Options } from 'ajv';
import { markRaw } from 'vue';
import { createAjv as createDefaultAjv } from '../../src';
import { ajvKeywords } from './keywords';

Expand All @@ -13,6 +12,5 @@ export const createAjv = () => {
const ajv = createDefaultAjv(options);
ajvKeywords(ajv);

// when ajv is used in component properties do not make it reactive
return markRaw(ajv);
return ajv;
};
7 changes: 1 addition & 6 deletions packages/vue-vuetify/dev/views/ExampleView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import cloneDeep from 'lodash/cloneDeep';
import find from 'lodash/find';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import {
markRaw,
onMounted,
provide,
ref,
Expand Down Expand Up @@ -66,7 +65,7 @@ const initialState = (exampleProp: ExampleDescription) => {

// Get custom renderers for this example (if any)
const customRenderers = getCustomRenderersForExample(example.name);
const renderers = markRaw([...customRenderers, ...extendedVuetifyRenderers]);
const renderers = [...customRenderers, ...extendedVuetifyRenderers];

return {
data: example.data,
Expand Down Expand Up @@ -281,10 +280,6 @@ const handleAction = (action: Action) => {
if (action) {
const newState = action.apply(state);
if (newState) {
if (newState.renderers) {
newState.renderers = markRaw(newState.renderers);
}

Object.assign(state, newState);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ import { IsDynamicPropertyContext } from '@/util/inject';
import {
computed,
defineComponent,
markRaw,
provide,
ref,
unref,
Expand Down Expand Up @@ -433,12 +432,12 @@ export default defineComponent({

return {
validationMode: validationMode,
i18n: i18n ? markRaw(i18n) : i18n,
middleware: middleware ? markRaw(middleware) : middleware,
i18n,
middleware,
t,
mdAndUp,
vuetifyProps,
ajv: ajv ? markRaw(ajv) : ajv,
ajv,
control,
styles,
appliedOptions,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { clearAllIds, createAjv } from '@jsonforms/core';
import { markRaw } from 'vue';
import { extendedVuetifyRenderers } from '../../../src';
import { mountJsonForms } from '../util/util';

Expand All @@ -27,7 +26,7 @@ describe('AdditionalProperties nested AJV', () => {

it('mounts a map whose key pattern is only valid without the `u` flag', () => {
// A parent form configured with `unicodeRegExp: false`
const ajv = markRaw(createAjv({ unicodeRegExp: false }));
const ajv = createAjv({ unicodeRegExp: false });
expect(() =>
mountJsonForms(
{ secretFiles: {} },
Expand Down
3 changes: 1 addition & 2 deletions packages/vue-vuetify/tests/unit/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import TestComponent from './TestComponent.vue';
import { createVuetify } from 'vuetify';
import * as components from 'vuetify/components';
import * as directives from 'vuetify/directives';
import { markRaw } from 'vue';

const vuetify = createVuetify({
components,
Expand All @@ -37,7 +36,7 @@ export const mountJsonForms = (
schema,
uischema,
config,
renderers: markRaw(renderers),
renderers,
i18n,
ajv,
},
Expand Down
38 changes: 30 additions & 8 deletions packages/vue/src/components/JsonForms.vue
Comment thread
KiaraGrouwstra marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@
</template>

<script lang="ts">
import { PropType, reactive, defineComponent } from 'vue';
import {
PropType,
reactive,
shallowReactive,
shallowRef,
toRaw,
defineComponent,
} from 'vue';
import {
coreReducer,
Actions,
Expand Down Expand Up @@ -122,6 +129,19 @@ export default defineComponent({
},
},
emits: ['change'],
/**
* The state of a form is shallow.
*
* JSON Forms only reads the schema and the UI schema, thus Vue does not need
* to make them reactive. A deep proxy made a new proxy for each nested schema
* node at the first read of that node. Testers and the scope resolution read
* the schema many times for each control, thus these proxies made a large
* form slow to mount.
*
* `shallowRef` and `shallowReactive` keep the objects of the application as
* they are. A reducer replaces `core` for each change and never changes it in
* place, thus shallow tracking is sufficient.
*/
data() {
const dataToUse = this.data;
const generatorData = isObject(dataToUse) ? dataToUse : {};
Expand All @@ -140,18 +160,19 @@ export default defineComponent({
initialCore,
Actions.init(dataToUse, schemaToUse, uischemaToUse, {
validationMode: this.validationMode,
ajv: this.ajv,
// Ajv cannot compile a schema through a proxy of itself.
ajv: toRaw(this.ajv),
additionalErrors: this.additionalErrors,
}),
coreReducer
);
return core;
};
return {
schemaToUse,
dataToUse,
uischemaToUse,
jsonforms: {
schemaToUse: shallowRef(schemaToUse),
dataToUse: shallowRef(dataToUse),
uischemaToUse: shallowRef(uischemaToUse),
jsonforms: shallowReactive({
core: initCore(),
config: configReducer(undefined, Actions.setConfig(this.config)),
i18n: i18nReducer(
Expand All @@ -166,7 +187,7 @@ export default defineComponent({
cells: this.cells,
uischemas: this.uischemas,
readonly: this.readonly,
},
}),
};
},
computed: {
Expand Down Expand Up @@ -243,7 +264,8 @@ export default defineComponent({
this.uischemaToUse,
{
validationMode: this.validationMode,
ajv: this.ajv,
// Ajv cannot compile a schema through a proxy of itself.
ajv: toRaw(this.ajv),
additionalErrors: this.additionalErrors,
}
),
Expand Down
Loading