davidkpiano / react-redux-form

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

Using createForms() correctly with dynamically created forms #852

Open jcheroske opened 7 years ago

jcheroske commented 7 years ago

I'm wanting to simplify my store config, and am experimenting with using RRF in a completely dynamic fashion. Here's how I'm setting up my root reducer:

    const rootReducer = combineReducers({
      apollo: apolloClient.reducer(),
      ...createForms({}),                     // <--- Not passing in any initial state
      ...sharedReducers,
      ...appReducers
    })

This actually works, but I'm not sure if I've suddenly wandered into the wilderness or not. What are some things I'm going to need to watch out for? How can I set the initial state of the form when doing things this way?

Update: Actually, that doesn't seem to work. Validation isn't running as expected. So, I guess what I'm really asking is: is it possible to have all forms dynamically added to RRF, or do I need to declare them all in the reducer?

davidkpiano commented 7 years ago

Can you share a reproducible example? It should work. When you fire an actions.change('foo.bar', { ... }), it should automatically create the form and field states for foo and foo.bar in the forms slice of your state.

jcheroske commented 7 years ago

Yeah, it did that. But for some reason, the validators were saying the form was invalid when it wasn't. As soon as I added the initial state of the model back to createForms it worked.

davidkpiano commented 7 years ago

I'd need to see a full code example.

jcheroske commented 7 years ago
import {FlatButton, TextField} from 'material-ui'
import PropTypes from 'prop-types'
import {Form, Control} from 'react-redux-form'
import {isEmail, isEmpty, isLength, normalizeEmail, trim} from 'validator'

const propTypes = {
  finalCost: PropTypes.number.isRequired
}

const required = val => val && !isEmpty(trim(val))
const minLength = min => val => val && isLength(trim(val), {min})
const email = val => val && isEmail(normalizeEmail(val) || '')

const ContactForm = ({finalCost}) => (
  <div>
    <h2>Your Quote: ${finalCost.toFixed(2)}</h2>

    <Form
      model='forms.contactForm'
      onSubmit={(...args) => console.log('burp', args)}
      validators={{
        name: {
          required,
          minLength: minLength(3)
        },
        email: {
          email
        },
        phone: {
          required,
          minLength: minLength(10)
        },
        commodity: {
          required,
          minLength: minLength(3)
        }
      }}
    >
      <Control.text
        component={TextField}
        floatingLabelText='Full Name'
        fullWidth
        mapProps={{
          errorText: ({fieldValue: {errors, submitted}}) => {
            if (submitted && errors.required) {
              return 'Full name is required'
            }
            else if (submitted && errors.minLength) {
              return 'Full name must be 3 or more characters'
            }
          }
        }}
        model='.name'
      />
      <Control.text
        component={TextField}
        floatingLabelText='Email Address'
        fullWidth
        mapProps={{
          errorText: ({fieldValue: {errors, submitted}}) => {
            if (submitted && errors.email) {
              return 'A valid email address is required'
            }
          }
        }}
        model='.email'
      />
      <Control.text
        component={TextField}
        floatingLabelText='Phone Number'
        fullWidth
        mapProps={{
          errorText: ({fieldValue: {errors, submitted}}) => {
            if (submitted && errors.required) {
              return 'Phone number is required'
            }
            else if (submitted && errors.minLength) {
              return 'Phone number must be 10 or more characters'
            }
          }
        }}
        model='.phone'
      />
      <Control.text
        component={TextField}
        floatingLabelText='Commodity'
        fullWidth
        mapProps={{
          foo: props => console.log(props),
          errorText: ({fieldValue: {errors, submitted}}) => {
            if (submitted && errors.required) {
              return 'Commodity is required'
            }
            else if (submitted && errors.minLength) {
              return 'Commodity must be 3 or more characters'
            }
          }
        }}
        model='.commodity'
      />

      <Control.button
        component={FlatButton}
        fullWidth
        label='Contact Us!'
        mapProps={{
          disabled: ({fieldValue: {valid}}) => !valid
        }}
        model='.'
        type='submit'
      />
    </Form>
  </div>
)

ContactForm.propTypes = propTypes

export default ContactForm

Here's my current reducer:

    const rootReducer = combineReducers({
      apollo: apolloClient.reducer(),
      forms: combineForms({}, 'forms'),
      ...sharedReducers,
      ...appReducers
    })

If an empty object is passed to combineForms then it doesn't work. If I pass in the initial state for the contactForm then it validates.

Bernix01 commented 7 years ago

any update on this? @davidkpiano @jcheroske

davidkpiano commented 7 years ago

Sorry, I'd need to see a full code example in a CodePen or CodeSandbox (makes it way easier to debug).

smattt commented 6 years ago

Ping... Interested in seeing if this was resolved or not.

I'm also reworking how we handle forms now and have been recently moving towards a more dynamic model, where I download N form configurations at runtime and would like to inject them into RRF. To further complicate this, one key change I'm trying to make work is that I'd like to store all field values in a single, shared reducer state, this will keep field values in sync across forms if they share an underlying ID - which is a business requirement. I'd like to dynamically inject forms at runtime, which use a slice of the shared reducer state, but ideally still produce unique form state for each dynamic form (so I can quickly and easily check form completeness, for example).

I'll plow ahead on my own but I'd be interested in hearing if you've already overcome any of these challenges, OP.

Thanks!