I tried to create a bug for this, but kept on getting an error when submitting.
Describe the bug
packages\react\src\JsonForms.tsx componentDidUpdate compares the previous schema and the props schema using the string inequality operator. If the two references are different, but the schemas’ content is the same, this will cause the function to continue executing. These schemas should probably be deep compared.
The next problem is that the setState call does not update end up updating the ID of the component in the DOM.
The IDs are managed in the Core package in packages\core\src\util\ids.ts. There is a Set usedIds that stores the used IDs and there a functions createId and removeId to update usedIds.
Expected behavior
packages\react\src\JsonForms.tsx componentDidUpdate should deep compare the schemas.
If the schemas are different, the setState call should update the component’s ID in the DOM.
Steps to reproduce the issue
- Define a JsonSchema as shown below. This represents a card.
{
"type": "object",
"properties": {
"employeeNumber": {
"type": "number",
"maximum": 1000
},
"firstName": {
"type": "string",
"isNotEmpty": true
},
"lastName": {
"type": "string",
"isNotEmpty": true
},
"maritalStatus": {
"type": "string",
"oneOf": [
{
"const": "SEPARATED",
"title": "Separated"
},
{
"const": "MARRIED",
"title": "Married"
},
{
"const": "DIVORCED",
"title": "Divorced"
},
{
"const": "SINGLE",
"title": "Single"
}
]
}
},
"required": [
"firstName",
"lastName"
]
}
- Define UI schema. This represents our card list
{
"type": "CardListControlV2",
"scope": "#/properties/employeeList",
"displayElements": [
{
"type": "Control",
"scope": "#/properties/firstName",
"options": {
"viewOnly": true
}
},
{
"type": "Control",
"scope": "#/properties/lastName",
"options": {
"viewOnly": true
}
},
{
"type": "Control",
"scope": "#/properties/employeeNumber",
"options": {
"viewOnly": true
}
},
{
"type": "Control",
"scope": "#/properties/maritalStatus",
"options": {
"viewOnly": true
}
}
],
"editorElements": [
{
"type": "Control",
"scope": "#/properties/firstName"
},
{
"type": "Control",
"scope": "#/properties/lastName"
},
{
"type": "Control",
"scope": "#/properties/employeeNumber"
},
{
"type": "Control",
"scope": "#/properties/maritalStatus"
}
]
}
- Initialize the card list to have two cards. The firstName fields will be given IDs similar to ‘firstName’ and ‘firstName2’. usedIds now has firstName and firstName2.
- We then add a card in a modal dialog. The firstName field in the dialog will have an ID equal to ‘firstName3’. Close the dialog. usedIds now has firstName, firstName2, and firstName3.
- The new card is added to the card list. The firstName field in the new card will have an ID equal to ‘firstName4’. usedIds now has firstName, firstName2, firstName3 and firstName4.
- The modal fields are unmounted, and the IDs are removed from usedIds. usedIds now has firstName, firstName2, and firstName4.
- For the new card, componentDidUpdate now compares prev schema and props schema and thinks they are different since the references are different, but they are actually the same. removeId is called, so firstName4 is removed from usedIDs. createId is called. firstName3 is created, but the new card’s ID is not updated in the DOM and remains firstName4. usedIds now has firstName, firstName2, and firstName3.
- We now view the new card in the modal dialog. The firstName field is assigned firstName4. Now there are two firstName4s in the DOM: one in the new card in the card list and the other in the modal dialog.
Additional context
We are using both the Core and the React packages. Both v3.4.1.