Skip to content
This repository has been archived by the owner on Mar 23, 2023. It is now read-only.

Stores and form validation? #2

Closed
justinbelcher opened this issue Jul 26, 2014 · 7 comments
Closed

Stores and form validation? #2

justinbelcher opened this issue Jul 26, 2014 · 7 comments

Comments

@justinbelcher
Copy link

There are times when I have form components changing data in a particular domain, and the business logic that governs validation and error states is sufficiently complex. Where is the best place to put that logic?

It seems reasonable to place the logic in Stores so that validation is centralized and easy to test. And those stores can fire error actions an ErrorStore can collect, log, and act upon. But it's unclear to me how you'd surface those errors back to the components that created them.

Another approach is to perform the validation in form components themselves, which seems reasonable for basic validation, but for complex business logic that feels like the wrong place for it.

Any guidance for handling this case?

@sterpe
Copy link

sterpe commented Jul 26, 2014

Wouldn't the presence or absence of errors just be treated as part of the state data that a view-controller is suppose to render? Ie, keep the render fn pure?

Sent from my iPhone

On Jul 25, 2014, at 10:17 PM, Justin Belcher notifications@github.com wrote:

There are times when I have form components changing data in a particular domain, and the business logic that governs validation and error states is sufficiently complex. Where is the best place to put that logic?

It seems logical to place the logic in Stores so that validation is centralized and easy to test. And those stores can fire error actions an ErrorStore can collect, log, and act upon. But it's unclear to me how you'd surface those errors back to the components that created them.

Another approach is to perform the validation in form components themselves, which seems reasonable for basic validation, but for complex business logic that feels like the wrong place for it.

Any guidance for handling this case?


Reply to this email directly or view it on GitHub.

@justinbelcher
Copy link
Author

In many cases that's practical, particularly for less specific errors. When several components are able to change the same chunk of data it becomes a little less clear how to get the right error from the store back to the right component that initiated the action.

I suppose this might also be a good use case for more granular stores. I.e. a FooStore and a partner FooFormStore which can stand between input and the pure data of FooStore, and surface errors back to the form view-controller.

@sterpe
Copy link

sterpe commented Jul 26, 2014

Yeah, another possibility is to pass a component specific errback as part of the payload. If the store errors processing the payload it cleans itself up and fires the appropriate next action if any and the component specific errback. I could see the argument there being that it violates flux principle

Maybe it can be made more flux like by actually throwing errors from stores that bubble back to the dispatcher...I think the dispatch function would have to be specially instrumented to take a reference to the component as well as a payload or a component ref as part of the default payload .

Dispatcher injects a payload into the dispatch stream targeted to the error store which fires error event details only to component that initiated the erroring action?

Sent from my iPhone

On Jul 26, 2014, at 6:51 AM, Justin Belcher notifications@github.com wrote:

In many cases that's practical, particularly for less specific errors. When several components are able to change the same chunk of data it becomes a little less clear how to get the right error from the store back to the right component that initiated that action.

I suppose this might also be a good use case for more granular stores. I.e. a FooStore and a partner FooFormStore which can stand between input and the pure data of FooStore, and surface errors back to the form view-controller.


Reply to this email directly or view it on GitHub.

@sterpe
Copy link

sterpe commented Jul 26, 2014

I suppose one way flow means error bubbling back to the dispatch is out...still if the component registers a unique id with an error store and attaches that id to all action payloads it initiates a store can error on the input, dispatch an error action on the payload which ends up in the error store which can pass the error on to the specific component registered to the unique id.

Sent from my iPhone

On Jul 26, 2014, at 8:59 AM, srterpe@gmail.com wrote:

Yeah, another possibility is to pass a component specific errback as part of the payload. If the store errors processing the payload it cleans itself up and fires the appropriate next action if any and the component specific errback. I could see the argument there being that it violates flux principle

Maybe it can be made more flux like by actually throwing errors from stores that bubble back to the dispatcher...I think the dispatch function would have to be specially instrumented to take a reference to the component as well as a payload or a component ref as part of the default payload .

Dispatcher injects a payload into the dispatch stream targeted to the error store which fires error event details only to component that initiated the erroring action?

Sent from my iPhone

On Jul 26, 2014, at 6:51 AM, Justin Belcher notifications@github.com wrote:

In many cases that's practical, particularly for less specific errors. When several components are able to change the same chunk of data it becomes a little less clear how to get the right error from the store back to the right component that initiated that action.

I suppose this might also be a good use case for more granular stores. I.e. a FooStore and a partner FooFormStore which can stand between input and the pure data of FooStore, and surface errors back to the form view-controller.


Reply to this email directly or view it on GitHub.

@justinbelcher
Copy link
Author

Yeah that's also true. I suppose I was looking to avoid adding and bookkeeping GUIDs on every component that could cause an error state, but that might be the required to stay truly fire and forget in the views.

@fisherwebdev
Copy link
Contributor

I think I would normally create a utility library like FooValidationUtils.js that could determine whether the input was valid based on data passed into the React component -- and I would pass down whatever data was required to judge the validity of the input. This would encapsulate the logic required for validation.

The Utils modules I have in mind typically have no dependencies on Stores, so that they can be used in any context -- any part of the system, view or store, can invoke these utility methods. The methods should be stateless and pure, in the functional sense. So then you could do view-level validation and store-level invariant() enforcement with the same Utils functions. Unit testing these is easy and straightforward.

Also, if the individual form input components need to be reused, but have different validation logic in different contexts, you will want to pass event handlers to them as props, allowing the parent components to define how the validation should be done.

If the amount of data required to determine validity is really quite large and complex, and thus difficult to pass down through the component tree (which I'm having a little trouble imagining), then I might consider storing the validation state in the FooStore itself (maintaining a _formErrors object), or a FormStore or an ErrorStore as suggested by @sterpe. For the unique IDs, you may want to look at using React's this.refs.

But this seems like it isn't ideal for most validation. Form inputs are, after all, one of the few places where maintaining state in the React component is a good thing, and it seems appropriate to perform validation on that state before creating a new Action.

Moving validation to the stores makes more sense to me if you want to create a UI where the user is free to put the stored data into an invalid state, and work with the UI in that invalid state for a while. For example, an image picker where the user must pick a minimum of 5 images. But it's okay for them to have 3 images picked while they are browsing images, with the save button grayed out. Then your controller-views would call FooStore.hasEnoughImages() in the handler for the Store's change event to get that boolean flag.

@justinbelcher
Copy link
Author

Makes perfect sense. Thanks for the great insights @sterpe and @fisherwebdev. I believe validation utils and allowing the UI to operate in an invalid state before persisting will work great in our case.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants