kettanaito / react-advanced-form

Functional reactive forms. Multi-layer validation, custom styling, field grouping, reactive props, and much more.
https://redd.gitbook.io/react-advanced-form
MIT License
217 stars 24 forks source link

Formless fields #216

Open kettanaito opened 6 years ago

kettanaito commented 6 years ago

What

Need to consider a formless usage of field components.

Why

Right now when a field component is rendered and is unable to find any ancestor Form, it will throw an error during the record registration. This may be confusing for the beginning developers.

However, it is also reasonable to insist to wrap fields in Form. Fields compose a form, so there must be a form. This suggestion requires discussion.

Specification

  1. General statements. a) A field is considered formless when it doesn't have a wrapping <Form> component.
  2. Technical statements. a) A formless field cannot access fields and form within any callback/handler methods. b) A formless field cannot access fields and form in any validation resolvers. c) A formless field behaves as a plain input field. That implies, it is uncontrolled by default, and can be made controlled by providing a value prop and a change handler function. d) (???) A formless field can still accept field enhancers. e) A formless field doesn't benefit from any form behaviors (serialization, validation, etc.).
kettanaito commented 6 years ago

The only reason I find for this is for the field components to be really reusable anywhere in the code. That what you expect from the components you declare, not only form-related.

As a consequence of being formless, you won't be able to access fields or form in the callback methods, and the fields are going to be uncontrolled.

kettanaito commented 6 years ago

This change also means decentralization of the field events handling, and event callbacks.

As for now, any field interaction emits the respective event, the latter is being caught by the form observables, and being handled properly. Form is also the place responsible for dispatching various callback methods (i.e. onChange, onFocus) provided by the end developer to react to field updates. In case the form isn't present, there is:

I don't know how I feel about this overview. This most likely means a huge rework of the architecture, and I think it's not worth the trouble.


Alternatively, formless fields may be just super diminished versions of full-scale fields. I am okay with the changes if it's approximately like this:

if (this.context.form) {
  return handleFieldAsNormal();
}

handleFieldAsFormless();
kettanaito commented 6 years ago

Summing things up.

The issues

1. Where to keep the field's state once it's formless?

Now the fields' state are kept in the form's state. Once the form is gone, formless field must contain its own state by itself.

Solution

Formless fields are stateful, containing their own field state.

2. How to mutate the field's state?

All field state mutation actions (fieldChange, fieldBlur, etc.) are kept in the form. Formless field must use some other way of mutating the field's state.

Solution

I keep thinking of pure functions which take the current field state, additional data, and produce the next field state. This would be a functional way of form implementation. Then, the form component itself would reuse those pure functions as well.

A couple of pure function examples:

function handleFieldChange({ fieldProps, fields, form, nextValue }) {
  return fieldProps.set(fieldProps.get('valuePropName'), nextValue);
}

function validateField({ fieldProps, fields, form }) {
  const nextValidityState = fieldUtils.validate({ ... });
  return fieldProps.merge(nextValidityState);
}
kettanaito commented 6 years ago

Update

It has just occurred to me, that it is possible to understand whether the field is formless on the createField high-order component level.

That way, in case of formless fields, HOC could act as a field's state accumulator, a replacement for Form in terms of field-form communication. This way field component remains as is, it keeps communicating up as is, as if there is always a form. Instead, createField HOC gets smarter in case of formless fields.

kettanaito commented 6 years ago

@Vidlec In case you started to work on this feature, please align with me.

I'm preparing the architectural changes to RAF (#250), one of the points of which is to isolate the whole logic into bunch of pure functions. That will help tremendously to use the same composite handlers on the createField wrapper, just as a form handles the field.

Let me know if anything!

nickensoul commented 5 years ago

Any news on this? I just can't catch this thing – why React validation libs coerce us to use input always within a form element? Am I miss something?

I'd like to use createField HOC as a generic common wrapper within a custom UI-kit components, unwrapped in any form at all.

Maybe renderless component, something like FormWrapper, or another HOC e.g. withForm should do the trick? it seems to be more convenient and flexible.

Big up and thanks for development this part of UI\UX with React!

kettanaito commented 5 years ago

Hi, @nickensoul. Thanks for the kind words! Let me update you.

Technical background

The biggest task here is to replace the Form as the event source hub. At the moment the Form component establishes per-instance event emitter, and subscribes to certain events with handlers. Field component wrapped in createField communicates back to the parent Form via form's event emitter to propagate such events as mounting/unmounting, value change, focus, or blur.

I would say that from the technical perspective a solution to support formless fields is somewhat clear. It would be good to implement (or prepare for) formless fields under #324. I'm currently working on #354, which improves event handlers that would grant less control to the Form, and more to the events.

What now?

I understand both a desire to use a form element without a wrapping Form, and a conceptual idea behind a form input (to gather data for a form). Nevertheless, I still think that formless fields is worth implementing, but I would also encourage developers to expect less functionality from a formless field.

This implementation is on the roadmap, but I would put #324 and #232 as the highest priority.

Functionality limitations

nickensoul commented 5 years ago

Wow, appreciate such fast and detailed reply! Thanks, @kettanaito. Yep, I've understood points you've mentioned above and agree with them. I just need a one confirmation: as I understood, at this time, we shell use react-advanced-form as its current state – wrap fields in form; but in nearest future we'd like to refactor them after #324 (or later task) will be released? Is there are breaking changes expected?

redraushan commented 5 years ago

Considering the limitations that @kettanaito has mentioned, I don’t think making any field formless should introduce any breaking changes, also I am assuming that it should respect the global validation rules and messages.

kettanaito commented 5 years ago

@nickensoul, yes, your understanding is correct.

We use RAF on production, and at the moment we wrap each field we tend to use outside of a form with an explicit Form component to ensure proper communication (such use cases are few, however). The future plan is to refactor this communication, which would potentially allow formless fields. As I've mentioned above, createField itself is already a HOC, which can check if there is a parent form, and if not, it can serve as the events hub instead of a form. A proof of concept wouldn't hurt here.

True, @redraushan, it shouldn't be a breaking change. Communication updates and adoption of new context API will cause changes internally, but I would strive to sustain the existing API as-is. There is already a list of suggested API change proposals for 2.0 – #264, I would list any breaking change after this migration there.