I am using the react-hook-form library to create a multi-step dynamic large form (over 70 fields). Most of my form fields are dynamic (user can add/delete more fields by clicking + button). In order to achieve this, I use react-hooks and add a hook for each dynamic field. But I feel like my code is not abstract enough and there might be a smarter way to do this. Below there is an example screenshot of my form:

Respective code for the hooks and form fields are here:
// hooks
const [sizes, setSizes] = React.useState([0]);
const [formats, setFormats] = React.useState([0]);
const [counter, setCounter] = React.useState(1);
// buttons
const addSizesFieldset = () => {
setSizes(prevIndexes => [...prevIndexes, counter]);
setCounter(prevCounter => prevCounter + 1);
};
const removeSizesFieldset = index => () => {
setSizes(prevIndexes => [
...prevIndexes.filter(item => item !== index)
]);
setCounter(prevCounter => prevCounter - 1);
};
const addFormatsFieldset = () => {
setFormats(prevIndexes => [...prevIndexes, counter]);
setCounter(prevCounter => prevCounter + 1);
};
const removeFormatsFieldset = index => () => {
setFormats(prevIndexes => [
...prevIndexes.filter(item => item !== index)
]);
setCounter(prevCounter => prevCounter - 1);
};
// form field arrays
{sizes.map(index => {
const fieldName = `sizes[${index}]`;
return (
<fieldset name={fieldName} key={fieldName} >
<h4>Sizes:</h4>
<label>
Size:
<input
type="text"
placeholder="Input"
name={`${fieldName}.size`}
ref={register}
/>
</label>
<button type="button" onClick={addSizesFieldset}>
+
</button>
<button type="button" onClick={removeSizesFieldset(index)}>
-
</button>
</fieldset>
);
})}
{formats.map(index => {
const fieldName = `formats[${index}]`;
return (
<fieldset name={fieldName} key={fieldName} >
<h4>Formats:</h4>
<label>
Format:
<input
type="text"
placeholder="Input"
name={`${fieldName}.format`}
ref={register}
/>
</label>
<button type="button" onClick={addFormatsFieldset}>
+
</button>
<button type="button" onClick={removeFormatsFieldset(index)}>
-
</button>
</fieldset>
);
})} ´´
Imagine you are doing these hook instants and const add/remove...Fieldset functions for seventy fields. It seemed too much duplication to me but I couldn't find any solution or a new idea other than "React can handle multiple hooks so no worries" comments.
If you want to try your ideas on codesandbox, I prepared below code as well:
https://codesandbox.io/s/react-hook-form-wizard-form-yzno5
Maybe if you use useReducer, it would be better if you have multiple states
const initialState = {
counter: 1,
sizes: [0],
formats: [0],
};
function reducer(state, action) {
switch (action.type) {
case 'add':
return {
...state,
counter: state.counter + 1,
[action.typeObject]: [...state.sizes, state.counter + 1]
};
case 'remove':
return {
...state,
counter: state.counter - 1,
[action.typeObject]: [...state.sizes.filter(item => item !== action.index)]
};
default:
return state;
}
}
/***/
const [stateReducer, dispatch] = React.useReducer(reducer, initialState);
/***/
const addSizesFieldset = () => {
dispatch({ type: 'add', typeObject: 'sizes' })
};
const removeSizesFieldset = index => () => {
dispatch({ type: 'remove', typeObject: 'sizes', index })
};
const addFormatsFieldset = () => {
dispatch({ type: 'add', typeObject: 'formats' })
};
const removeFormatsFieldset = index => () => {
dispatch({ type: 'remove', typeObject: 'formats', index })
};
Here is my simple implementation by forking your code: https://codesandbox.io/s/react-hook-form-wizard-form-1h4yq
You can even remove 2 functions by adding "typeObject" in param:
const addFieldset = field => () => {
dispatch({ type: 'add', typeObject: field })
};
const removeFieldset = (index, field) => () => {
dispatch({ type: 'remove', typeObject: field, index })
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With