简体   繁体   中英

Stores for reusable React+Flux form component (one component for multiple forms with different fields)

In an application I'm working on I have a couple different forms with different fields. I'd like to create one reusable component to use for each form.

  • Form #1 could have the following fields: email, password
  • Form #2 could have the following fields: mobile messaging, tablet messaging, desktop messaging, background color, text color

For this example -- let's say I'm using Form #1; here's my code for that use case:

var LoginForm = React.createClass({
    handleSubmit: function(){
        var formValues = {
            username: '',
            password: '',
            url:      '/api/login/'
        };

        // Submit form, whee!
        FormActions.submit(formValues);
    },
    render: function(){
        var formSections = [
            {
                name: 'Sign in here',
                fields: [
                    {
                        name: 'username',
                        label: 'Username',
                        type: 'text'
                    },
                    {
                        name: 'password',
                        label: 'Password',
                        type: 'password'
                    }
                ]
            }
        ];
        return (
            <FormComponent sections={formSections} handleSubmit={this.handleSubmit} />
        );
    }
});

So, I want to be able to pass my FormComponent an array containing form sections which contain fields. Examples of form sections would be: personal information, shipping address, billing details.

What I'm trying to figure out, is how can I generate a 'general' Flux store that will handle whatever form fields I throw at it. For example, in my formSections array, I'd like to be able to pass different fields and still utilize the Flux architecture.

What I'm not sure of is if I'm making a mistake using this pattern; should I be creating a different set of actions, constants, stores, etc for each type of form I'll be using? Or is it a solid practice to create one form component that's capable of handling different fields in a single FormComponentStore store?

I've implemented something like this in React/Flux, and am pretty happy with the results, though it may be a little too specialized for you to use in whole. I'll run through the moving parts briefly, and you can decide if you like the approach, and what parts might fit your needs. The basic idea is to be able to generate both Form DOM and the behavior it needs to communicate with different parts of my API from a blob of configuration. First, here's how I instantiate a BasicAPIForm, which is a stateful component backed up by a Flux "FormStore":

See code :

render: function(){
  var formProps = {
    formId: "uniqueFormId1",
    fieldsMeta: {
      name: {inputType: "text", label: "Name", required: true},
      description: {inputType: "textarea", label: "Description", required: true},
      url: {inputType: "text", label: "Website URL", required: true},
      email: {inputType: "text", label: "Contact Email", required: true},
      image: {inputType: "image", label: "Logo", required: false, filename: "logo.png"}
    },
    defaultValues: {
      name: "",
      description: "",
      url: "",
      email: "",
      image: null,

      imageData: null,
      actionState: "ready",
      message: ""
    },
    columns: [
      { fields: ['image'], className:'col-xs-5 col-sm-4 col-md-3' },
      { fields: ['name', 'description', 'url', 'email'], className:'col-xs-7 col-sm-8 col-md-9' }
    ],
    apiContext: {
      formId: this.props.type,
      apiCollectionKey: "theRightCollection",
      actionUrl: "/v1/resource/items",
      method: "POST",
      successHttpStatus: [201],
      successMessage: "New resource created"
    }
  };
  FormStore.getOrInitFormData(this.props.type, formProps);

  return (
    <div className="active-panel api-form image-upload-form clearfix">
      <div className="container-fluid">
        <BasicAPIForm {...formProps} />
      </div>
    </div>
  );
}

The BasicAPIForm component needs a unique formId , a listing of fields that it can understand (props like filename are required for the image upload field, for example), some initial state ( defaultValues ), some configuration on how to group the fields into columns, and then a similar API configuration object that helps the APIStore construct an API request and handle the response. BasicAPIForm is a complex component with a lot of behavior, as I've implemented it. For each different type of input (like 'image'), I had to create a Component. In BasicAPIForm's render() method, it basically churns through the columns, rendering the fields in each one according to configuration and current state. This is a "managed" form, so on each change to a DOM input, it updates its own state and syncs with the FormStore.

The FormStore as seen in the call FormStore.getOrInitFormData(this.props.type, formProps) keeps synced with the state for all the active forms on the application. This is potentially not needed; you could keep all state in the component instead.

The APIStore in our implementation is responsible for managing the data known to the front end app and communicating with the server to fetch or post needed data. The APIStore listens for form POST actions, gets the relevant input and API configuration from the FormStore, sends requests to the API, stores results, and creates a success or failure action on completion that the FormStore (and Form component) can pick up on to display results.

In our implementation of the FormStore, when state changes occur on the BasicAPIForm, the representation in the FormStore is patched. Storing different data for different forms was no obstacle, as long as the data basically looks like a JSON {} object and the form has a unique key that is passed with all calls to the FormStore. At first, I was storing default values and types in the Store, but that wasn't scaling well, so I moved it to this big configuration blob above that could live very close to the rendering code where it was relevant. There will surely be some big future changes to where this is put.

This approach continues to evolve on our project. So far, it took a long time to build the first time, but each successive refactor and new form added took less and less time, so overall, it seems increasingly productive. This justifies putting a high degree of interactivity and polish into the forms.

I can't comment so I must answer but it's really a simple question. Why you want to have FormStores?, the submit of a form shouldn't create a domain entity an then save that entity to a EntityXStore?

For example, im starting with a simple Flux application and i have a LoginForm too, but i have a UserStore for store (or not) the user if the login succeed.

Unless that in your specific case save fulfilled forms makes sense, for that reason I say that is a question.

Sorry for the english!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM