Form generators refactor - First steps and factored out FormInput#1176
Form generators refactor - First steps and factored out FormInput#1176
Conversation
…ed all forms to use it instead
…ded inputGenerator to start replacing the old getInputType
stephenhand
left a comment
There was a problem hiding this comment.
This is an awesome refactor.
Much happier that this is refactored internally in Flex rather than going into the form defs module, didn't really want a clatter of React deps going in there.
Although it might make sense to put them in a 3rd module at some point, but only when something else needs to consume them.
I would move the 'forms' directory out of the 'common' directory as part of this refactor. I would probably move everything else out of there too - there doesn't really seem to be any distinction between stuff in components/common and any of the other components...
|
|
||
| const allwaysEnabled = () => true; | ||
|
|
||
| const useCreateFormFromDefinition = ({ |
There was a problem hiding this comment.
I think we should decompose this into a hook and a simple function that the hook calls, and make them both public.
I've had to change the memoisation behaviour a number of times in the past on individual forms because the 'standard' behaviour is broken in some scenarios - I had some issues around checking whether the form was in the correct state to show the 'you have unsaved edits' dialog that fixing involved changing the memo change - which it probably drastically reduced the memoisation effectiveness but didn't noticeably affect performance.
So we should leave ourselves the option of hand rolling the hooks but still being able to reuse the core logic, should be a straightforward change.
|
FYI the E2E test failures seem to be related to your changes, rather than just flake |
I did a quick profiling analysis of our use of memo in the forms and this is what I found: I did some profiler tests of changing the values in fields on the child form on Aselo Development for a new offline contact. This is where I expect the memo optimisation to kick in, the values are changing in Redux kicking off a component render, but the memo prevents the form generation step from rerunning. Versions where the form had effective memoisation were faster than the current implementation - by approx 40ms each time moved away from a field you just updated, small but not trivial. However, as you say, the current implementation in many cases isn't no memoisation, it's ineffective memoisation. Removing the memoisation completely made it almost as fast as the memoised version, maybe 5-10ms slower. Removing the overhead of memoising in the first place saves almost as much as properly memoising. Couple of reasons why this is the case:
I experimented with some relatively straightforward optimisations we could do to speed up forms that seemed to have a reasonable impact, but I won't go into those here, it's a bit off topic. |
| export * from './components'; | ||
|
|
||
| export { default as useCreateFormFromDefinition } from './hooks/useCreateFormFromDefinition'; |
There was a problem hiding this comment.
Does it makes sense for this module to live in /components/forms or would you prefer this to go one level up, outside of components folder? I think we could treat forms as it's own module right?
Primary reviewer:
Description
This PR is a proposal to bring some order to our form generators module. It also includes some changes in the way they are generated.
createFormFromDefinitionfunction. This is instead replaced by a custom hookuseCreateFormFromDefinitionthat will receive mostly the same parameters andWill take care of memoizing the computed form resulting from the given form definition. This was previously a burden in the caller components, and in most cases after some time we ended up ruining the memoization anyways.To see the benefits of this change you can sit in commit b816e89, which is the current state of our forms plus a debug log. Interact with the tabbed forms while keeping an eye to the Chrome console and you'll see that the form is being re-built in every re-render of the parent component. Then sit in commit 328286f that already handles the creation of the form via this hook, and check that it is only created once, and then kept in sync via rerenders (but the form as such does not needs to be entirely rebuilt from the definition).
Removed after @stephenhand gathered empirical evidence that this memo is not really necessary :P
inputGenerator.tsx, withcreateInputfunction which is intended to replace the existinggetInputTypeone. It is intended to be simpler as the logic for each component should be factored out from it, leaving this function as the link between aFormItemDefinitionand it's corresponding input.forms/where I think we could "factor out" our custom inputs (right now they all live in the 1k lines filesrc/components/common/forms/formGenerators.tsx.tsx😅). Inside I've exemplified with top level filestypes.tsandstyles.tsx, where the code shared among all the component types will live. There's also aFormInput/module that containsFormInput.tsxandstyles.tsx, which make the base input that corresponds toFormInputType.Input. This is the easier one since it's the base style, but you get the idea. I'm also bundling the unit tests for this module within itself. Happy to move this to the usual__tests__folder if preferred by the team, I've just been thinking this way can be easier to work with.Why I think this refactor is a good idea (this are mostly my opinions):
src/styles/HrmStyles.tsx.createItemand the hook as separate things will be easier to handle than trying to write tests for the big monster we currently have 😆.Checklist
Related Issues
Fixes #....
Verification steps
Manual regression test to verify the forms generated (tabbed forms, case forms..) are still working as intended, no change in the behavior is expected.
updateCallbackis invoked as expected, properly updating the Redux state (if pertinent).