davidkpiano / react-redux-form

Create forms easily in React with Redux.
https://davidkpiano.github.io/react-redux-form
MIT License
2.07k stars 251 forks source link

RRF Lifecycle frustrations #821

Open mikkelwf opened 7 years ago

mikkelwf commented 7 years ago

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:

    • Render a form (with no schema) with remote, so the Control gets initialValue from the first value assigned
    • Unmount form, which in my case triggers a resetForm AC, resetting for form (and removing the Control value from store
    • 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!

davidkpiano commented 7 years ago

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!

mikkelwf commented 7 years ago

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.. :)

davidkpiano commented 7 years ago

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.

andrewhl commented 7 years ago

@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'),
});
andrewhl commented 7 years ago

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:

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.

davidkpiano commented 7 years ago

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.

jcheroske commented 7 years ago

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.

davidkpiano commented 7 years ago

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!

jcheroske commented 7 years ago

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:

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:

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.