[useForm]: eliminate tech debt
To Do
-
Remove state from the ref approach
Previously, we used refs to avoid closures inside hooks and to memoize the form API, ensuring that API callbacks were not recreated on every state change. This was necessary to prevent breaking user components wrapped inReact.memothat rely on form API methods. We can consider migrating to native state with setState callbacks to always have access to the latest value without needing to include form values in hook dependencies. However, we first need to address the issue of closures in theLensesimplementation. -
Enable storing form state outside the
useFormhook
AddvalueandonValueChangeprops to allow external control of the form state. When these props are provided, the form should operate based on them, similar to theuseFormStatehook. -
Lens improvements:
-
Resolve closure limitations:
The currentLensimplementation creates lenses only on mount, providing getters tied to the initial state:const lens = useMemo( () => new LensBuilder<T, T>({ get: () => formState.current.form, set: (_, small: T) => { handleFormUpdate(() => small); return small; }, getValidationState: getMergedValidationState, getMetadata: () => getMetadata(formState.current.form), }), [], );If we move away from the ref-based approach, this will prevent accessing updated values outside closures.
-
Memoize lenses:
Ensure lenses are memoized to prevent breaking components that receive lenses as props and are wrapped inReact.memo.
-
See #2683
I have a small request for improvements. While using lens, I've faced the need to access data within big while validating or updating small. WDYT, is it worth the effort?
I have a small request for improvements. While using lens, I've faced the need to access data within
bigwhile validating or updatingsmall. WDYT, is it worth the effort?
@Kuznietsov Do you have an example?
I have a small request for improvements. While using lens, I've faced the need to access data within big while validating or updating small. WDYT, is it worth the effort?
You can provide .onChange callback for the big lens and than make a small for this patched lens with your handler, like in this example:
https://uui.epam.com/documents?id=lenses&mode=doc&category=advanced#provide_your_own_setter
In this case your onChange callback we be invoked while small lens value was set and you will have access to the big lens data alongside.
For validation we also pass parent data to the customValidator callback in a second param, like in this example:
https://uui.epam.com/documents?id=form&mode=doc&category=components#complex_validation
This is what you are looking for or you have another cases?
I have a small request for improvements. While using lens, I've faced the need to access data within big while validating or updating small. WDYT, is it worth the effort?
You can provide
.onChangecallback for the big lens and than make a small for this patched lens with your handler, like in this example: https://uui.epam.com/documents?id=lenses&mode=doc&category=advanced#provide_your_own_setter In this case youronChangecallback we be invoked while small lens value was set and you will have access to the big lens data alongside.For validation we also pass parent data to the
customValidatorcallback in a second param, like in this example: https://uui.epam.com/documents?id=form&mode=doc&category=components#complex_validationThis is what you are looking for or you have another cases?
Thanks for the quick response.
Example:
Records in a table may be linked, and specific rules declare these relations. When the value is changed, there is a need to update the record's fields so as not to break the rules of relations or shift dates if the relation type is changed.
I've achieved this goal with big.onValueChange and in some case, with small.onValueChange((prev, current) => onValueChange(prev, current, big)), but I'd like to see some more idiomatic approach for the cases, when access to the other records is required, but there is no need for their modification.
It would prevent a loop over all records when only two specific ones are needed. This approach is very useful, when auto-filling fields, for example.
Maybe, something like:
small.onChange((prev, current) => {
const relatedRecord = this.big().prop(current.relatedId).get();
....
return updatedSmall;
});
Draft PR with useForm refactoring - https://github.com/epam/UUI/pull/2683