Environment: Using create-react-app with typescript, packaged with npm.
Code:
I have a simple react application which has a 'user' page and an 'admin' page. For now this is not attached to any API calls, instead I have an array of objects stored inside 'templates', each object is a 'template' with information about fields required, their datatypes, any restrictions etc. The user page should have an interface where the user can select which collection to use, and a form which renders based on this selection. It also has a submit button to add their entry somewhere - this is still in development.
Below is the snippet of code relating to this issue.
import { JSONEditor, ObjectSchema, Schema, ValueType } from "react-schema-based-json-editor";
type JSXNode = JSX.Element | null;
interface field {
"name": string,
"description": string,
"type": string,
"value": string | number,
"units": string,
"options": string[],
"validation": string
}
interface collection {
"id": number,
"version": string,
"name": string,
"beamline": string,
"owner": string,
"fields": field[]
}
templates: collection[] = [
{
"id": 1,
"version": "v1.0.0",
"name": "Ptychography",
"beamline": "I14",
"owner": "Testy McTestface",
"fields": [
{
"name": "composition",
"description": "Element composition of your sample",
"type": "string",
"value": "", // the default value to be used.
"units": "",
"options": []
},
{
"name": "weight",
"description": "Weight, in grams, of your sample",
"type": "number",
"value": 20, // the default value to be used.
"units": "g",
"options": []
},
{
"name": "motor",
"description": "The motor to be used - one of two.",
"type": "string",
"value": "", // the default value to be used.
"units": "",
"options": [
"motor1",
"motor2"
]
}
]
},
{
"id": 2,
"version": "v1.0.0",
"name": "Crystallography",
"beamline": "B07",
"owner": "Hakuna Matata",
"fields": [
{
"name": "composition",
"description": "Element composition of your sample",
"type": "string",
"value": "",
"units": "",
"options": []
},
{
"name": "state",
"description": "The state of matter to be used.",
"type": "string",
"value": "",
"units": "",
"options": []
},
{
"name": "motor",
"description": "The motor to be used - one of two.",
"type": "string",
"value": "motor1",
"units": "",
"options": [
"motor1",
"motor2"
]
}
]
}
}
// to be passed as input to JSONEditor element for schema.
function generateSchema(coll: collection | undefined): ObjectSchema | undefined {
const schemaProperties: { [name: string]: Schema } | undefined = coll ?
coll.fields.reduce<{ [name: string]: Schema }>((result, field) => {
const schema: Schema = {
type: field.type as "string",
description: field.description,
default: field.value,
enum: field.options.length > 0 ? field.options : undefined,
}
return {
[field.name]: schema,
...result
};
}, {}) : undefined;
if (schemaProperties && coll) {
const generatedSchema: ObjectSchema = {
title: coll.name,
type: "object",
properties: schemaProperties,
required: Object.keys(schemaProperties),
collapsed: false
};
return generatedSchema;
}
return undefined;
}
// to be passed as input to JSONEditor element for InitialValue.
function generateInitialVals(schema: ObjectSchema | undefined): ValueType | undefined {
if (schema) {
const initialVals = Object.entries(schema.properties).reduce<{ [name: string]: ValueType }>(
(result, pair) => {
const [k, v] = pair;
result[k] = v.default;
return result;
}, {}
)
return initialVals;
}
return undefined;
}
function MakeForm(props: { useCollection: collection | undefined }): JSXNode {
const coll: collection | undefined = props.useCollection;
const collSchema = generateSchema(coll);
const initialValue = generateInitialVals(collSchema);
const [values, setValues] = useState<ValueType>(initialValue)
if (!coll)
return (
<h1>Select a valid template</h1>
)
console.log("schema is: ", collSchema);
const updateValue = (value: any, isValid: boolean) => {
if (isValid) setValues({ ...value });
};
return (
<div>
<JSONEditor schema={collSchema as ObjectSchema} updateValue={updateValue} initialValue={initialValue} v-if="visible" theme="bootstrap5" />
</div>
);
}
function UserPage() {
const collections = [...templates.map(item => item.name)];
const [coll, setColl] = useState<string>("");
return (
<div className="App">
<header className="App-header">Welcome, User!</header>
<body className="App-body">
<SelectCollection collections={collections} coll={coll} setColl={setColl} />
<MakeForm useCollection={templates.find(c => c.name === coll)} />
<SubmitButton />
</body>
</div>
);
}
function App() {
return (
<div>
<NavigationBar />
<main>
<Routes>
<Route path="/" element={<h1>Home Page</h1>} />
<Route path="/admin" element={<AdminPage />} />
<Route path="/user" element={<UserPage />} />
</Routes>
</main>
</div>
)
}
In reality, the code from above is spread across multiple files, but is included here for completeness in one chunk. Finally, the index.tsx looks like the following:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import 'bootstrap/dist/css/bootstrap.css';
import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();
Expected:
When running npm start and navigating to /user, I expect to see a dropdown button (from my SelectCollection function component) which lets me select a template by name, i.e. 'Ptychography' or 'Crystallography'. When this is selected, the MakeForm component recieves a prop which is the actual collection corresponding to that name. It then should re-render and create a form based on a generated schema using this template. If I select another collection name from the dropdown which exists in the templates array such as 'Crystallography', I would expect the MakeForm component to re-render, and the resulting form from JSONEditor to change to reflect the changed schema.
Actual:
Once I select 'Ptychography', the form entries do not change if I then select 'Crystallography'. Instead, any form elements which are required only by 'Ptychography' now have a 'not exists' checkbox next to them. It seems as though the schema does not change, when in fact I can verify the schema is correctly being generated with console logging. Only if I refresh the page and select 'Crystallography' does the correct form show up.
It seems as though JSONEditor is just not re-rendering. A useEffect hook hasnt helped with this either.
Version: 8.3.0
Environment: Using create-react-app with typescript, packaged with npm.
Code:
I have a simple react application which has a 'user' page and an 'admin' page. For now this is not attached to any API calls, instead I have an array of objects stored inside 'templates', each object is a 'template' with information about fields required, their datatypes, any restrictions etc. The user page should have an interface where the user can select which collection to use, and a form which renders based on this selection. It also has a submit button to add their entry somewhere - this is still in development.
Below is the snippet of code relating to this issue.
In reality, the code from above is spread across multiple files, but is included here for completeness in one chunk. Finally, the index.tsx looks like the following:
Expected:
When running npm start and navigating to /user, I expect to see a dropdown button (from my SelectCollection function component) which lets me select a template by name, i.e. 'Ptychography' or 'Crystallography'. When this is selected, the MakeForm component recieves a prop which is the actual collection corresponding to that name. It then should re-render and create a form based on a generated schema using this template. If I select another collection name from the dropdown which exists in the templates array such as 'Crystallography', I would expect the MakeForm component to re-render, and the resulting form from JSONEditor to change to reflect the changed schema.
Actual:
Once I select 'Ptychography', the form entries do not change if I then select 'Crystallography'. Instead, any form elements which are required only by 'Ptychography' now have a 'not exists' checkbox next to them. It seems as though the schema does not change, when in fact I can verify the schema is correctly being generated with console logging. Only if I refresh the page and select 'Crystallography' does the correct form show up.
It seems as though JSONEditor is just not re-rendering. A useEffect hook hasnt helped with this either.