The key distinction lies in addressing not only the state of form values, but also managing the state of form shape/structure.
If you display the inputs by traversing a state object that represents the form's structure, adding a new input becomes simply adding a new entry to this state object. Manipulating this state object allows for easy addition or removal of input fields on the form. For instance, you could create something similar to this (in pseudo React code):
// Define state for math and algorithms inputs
const state = { math: [obj1, obj2], algorithms: [obj1, obj2] } // obj ~= {id, value}
// Render math inputs
const mathMarkup = state.math.map(obj => <input value={obj.value} onChange={...} />)
// Add handler for math field
const addMath = () => setState(prevState => ({ math: [...prevState.math, newObj]}))
Here is an example of such a form - codesandbox. It may not be an exact match to what you see on your screen, but the concept should be clear. I've only implemented the first two sections due to unclear requirements on your form so you can understand the approach. And there are no styles :shrug:
Additionally, you can separate out the renderXyz
methods into individual components and refine the state structure according to your specific needs.