React-redux-form: RRF Lifecycle frustrations

Created on 3 Jun 2017  路  9Comments  路  Source: davidkpiano/react-redux-form

It's not clear not forms/models/values are initialized, and in my app, it's gone to the point where i cannot trust RRF to perform as expected, so i've prepped at couple of questions to find out if i'm the idiot here, or that the lifecycle has some issues.

  1. When are the initial value set?
    Is it set upon first declaration, or when the whole form is initialized?

  2. Is it required (or at least recommended) to declare the schema up front?
    I'm currently not doing that for all my forms (mainly because some forms are used with models where the schema varies, so i cannot (or its kinda hard at least) set the schema when initializing the form.

  3. Is it possible to unload a form (and remove it from the store)..?
    As i see it, a lot of lifecycle issues with data that gets carried over or not working as expected could be avoided if there were some kind of action to completely unload the form from the store. Also being able to dynamically initialize a form would be nice (as far as i know this is not possible either, since all forms should be declared in the store upfront). This would basically make a Form a connected LocalForm. I cannot use a LocalForm instead, because i access the store data within the form itself, to manipulate flow and the components shown.

  4. Where does a Control get its initial value from?
    I've noticed that values some times gets carried over if I:

  5. Render a form (with no schema) with remote, so the Control gets initialValue from the first value assigned
  6. Unmount form, which in my case triggers a resetForm AC, resetting for form (and removing the Control value from store
  7. Render the same form (this time with only initial values). The store has no value/data for the Control, but the Control has the value from the previous data set.
    Am i the idiot here by not declaring a schema?
    Futher more this issue (or at least something rather similar) has been reported here #791, #798.

Hope you can help me clarify these issues..
Thanks!

question

Most helpful comment

Right, forms are notoriously complex on the web. There's so many different use-cases, it's hard to have a one-size-fits-all solution (but I try!!)

V2 will make the internals much simpler, while keeping mostly the same API. It will be written in TypeScript.

All 9 comments

When are the initial value set?

When the store is initialized, same as initial state in Redux.

Is it required (or at least recommended) to declare the schema up front?

Yes, but it's not required. Forms can be fully dynamic.

Is it possible to unload a form (and remove it from the store)..? ... I cannot use a LocalForm instead, because i access the store data within the form itself, to manipulate flow and the components shown.

You can always connect() a component that includes a <LocalForm>, nothing stopping you from doing that. And you have onUpdate and onChange hooks to do whatever you want to the updated form/model states inside a local form.

Where does a Control get its initial value from?

From the Redux store. Simple as that.

I'll investigate those issues you listed and see if they're relevant to the confusion you're facing. Sorry about that!

Thanks for the update..

I'm a bit sorry for the headline, frustrations, because generally i really enjoy this library. And i would guess that most of my personal struggles/frustrations really comes down to lack of understanding on how the library works. And given that forms are a pretty complicated to wrap you head around, its paramount that the tools you use don't add to the confusion.

I would love to help out updating/extending the documentation, if i can contribute in any way, but i need to be more confident with the library to do that.. :)

Right, forms are notoriously complex on the web. There's so many different use-cases, it's hard to have a one-size-fits-all solution (but I try!!)

V2 will make the internals much simpler, while keeping mostly the same API. It will be written in TypeScript.

@davidkpiano Related question. How do I initialize a form from state? Let's say I want to have an edit resource form. Do I initialize a reducer in this fashion? I feel like I need to implement something like the below to make this work. Am I overcomplicating things? Is there a simpler way? I just want to initialize a form with pre-existing values from state. Same idea as redux-form's initialize from state.

const initialEditPersonState = {
    birthday_at: new Date(),
    gender: 'select',
    relationship: 'select',
    first_name: '',
    last_name: '',
};

function editPersonReducer(state = initialEditPatientState, action) {

    switch (action.type) {
        case api.GET_PERSON_SUCCESS: {
            return action.payload.person;
        }
        case 'rrf/change': {
            const model = action.model.split('.');
            const value = model[model.length -1];
            return {
                ...state,
                [value]: action.value,
            }
        }
        default:
            return state;
    }
}

const appReducer = combineReducers({
    app,
    deep: combineForms({
        editPerson: editPersonReducer,
    }, 'deep'),
});

I answered my own question (I seem to do this a lot lately). You only need to define the reducer case for initializing. You don't need to handle 'rrf/change'.

@davidkpiano I want to say that I really love this library. You've done a great job with it. I've tried pretty much every react form library out there (formsy, redux-form, tcomb), and this is hands down the fastest and best implementation. It feels really natural to use. I will advocate it to everyone I know. It even solved issues we were having with redux-form and ReactNative. That said, I find your documentation to be painfully unclear about some really fundamental things. I've had to dig through the docs, your source code, and do my own experimentation to get certain behaviours to work.

Things I felt are unclear:

  • how to use react-redux-form/native's Control to handle Picker.Item child components (figured it out by guesswork and looking at Control.select in the docs)
  • how to use the same Control to handle custom, e.g. Native Base, form components (I figured it out by looking at the source code for native.js: you pass in the component directly and bypass Control.picker or whatever)
  • how to initialize from state (again, guesswork, piecing things together. It really wasn't clear to me from anything indicated in the docs. This seems like such a fundamental use case that it should have a section dedicated to itself. I happened by chance to read the section in Models on using a custom reducer and extrapolated from that.)

I may be willing to accept that I am perhaps somewhat more dense than your average developer using this library, but supposing even that I am only equally as dense as the average, you may want to address some of these concerns. I would be willing to submit a PR for the documentation if that would be helpful.

A PR would absolutely be helpful, especially for the Native parts. I don't use React Native primarily in my job, and RRF is supposed to be an abstraction that works naturally with it, but it would be good to have documentation on that. I will add a section on initializing from state in the documentation.

I just wanted to echo @mikkelwf's sentiments. As far as I'm concerned, this IS the react form library. That said, the state of forms in 2017 is still lacking in some areas, and a solution that integrates the best ideas from all the React form libs doesn't exist yet. For instance, I'm convinced that first-class schema support would be a huge boon. A model definition is not exactly a UI concern, and being able to share schemas across forms is huge. I get that error messages are a UI layer thing, but being able to specify reasonable defaults at a higher level, and then overriding them in the UI as necessary is my preferred strategy. Anyways, I'm letting you know that, as far as I'm concerned, you are the guy, and this is the lib, when it comes to React forms. I might not be able to do too many pull requests, but I'll do my best to give you constructive feedback. Thanks a lot for all of your efforts.

For instance, I'm convinced that first-class schema support would be a huge boon.

100% agree. Anything you can point to that demonstrates a great example of that?

A model definition is not exactly a UI concern, and being able to share schemas across forms is huge.

I agree with this too. I'm moving away from UI-centered models in V2.

I get that error messages are a UI layer thing, but being able to specify reasonable defaults at a higher level, and then overriding them in the UI as necessary is my preferred strategy.

What would an ideal API for this look like to you?

I'll do my best to give you constructive feedback. Thanks a lot for all of your efforts.

I sincerely appreciate it!

Well, first off, I would look at skaterdav85/validatorjs. It is the best validation lib that I've seen, by far. It totally hits the sweet spot on so many levels. For instance, right out of the box, you get:

  • Validation ordering: the order of the rules is the order that they will be processed, and obtaining the first rule that fails is easy. In your UI, you can just grab the first error and display that one and that's good for so many cases. For example, if you list required|min:5|email then you'll get the required error first, and when that's fixed, then you'll get the minLength.
  • Error messages: Messages are at the validator level, but can easily be overridden. Best of all worlds.
  • Template error messages: Not only can you override the entire error message, but you can just override attributes within the message.
  • Total flexibility: You can define a schema for a model, but then only pull out part of that schema and use it in a form. You might have a big model, but then have several forms that only mutate part of the model at a time. You could pick the parts of the schema you want and validate against that. So easy!

If you embed that lib within a larger schema, then you could define most of your form in one place, like mobx-form does. I'd love to see something like:

{
  name: {
    rules: 'required|min:4|max:40',
    label: 'Name', // arbitrary prop that can get passed down to components
    placeholder: 'Enter your name' // another arbitrary prop
  },
  email: {
    rules: 'required|email',
    label: 'Email',
    placeholder: 'Enter your email address'
  },
  password: {
    rules: 'required|min:4|max:20',
    label: 'Password',
    placeholder: 'Enter a password'
  },
  passwordConfirm: {
    rules: 'required|same:password', // the awesomeness here is blinding
    label: 'Confirm Password',
    placeholder: 'Confirm your password'
    errorMsg: 'Both passwords must match!'
  }
}

If then, in the Control component, you make all of that available to the wrapped Component, that's some seriously powerful stuff. The component could have access to:

  • The first error message, if there is one
  • All those extra props (label, placeholder, etc.)

At that point, there really isn't all that much that needs to happen at the component level. You could do specific overrides there (pass in a label prop for example), but most of the heavy-lifting has been done by the schema+validation.

Was this page helpful?
0 / 5 - 0 ratings