react-redux-form icon indicating copy to clipboard operation
react-redux-form copied to clipboard

Using createForms() correctly with dynamically created forms

Open jcheroske opened this issue 7 years ago • 7 comments

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?

jcheroske avatar Jun 20 '17 21:06 jcheroske

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.

davidkpiano avatar Jun 20 '17 22:06 davidkpiano

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.

jcheroske avatar Jun 20 '17 22:06 jcheroske

I'd need to see a full code example.

davidkpiano avatar Jun 20 '17 22:06 davidkpiano

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.

jcheroske avatar Jun 20 '17 22:06 jcheroske

any update on this? @davidkpiano @jcheroske

Bernix01 avatar Sep 14 '17 21:09 Bernix01

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

davidkpiano avatar Sep 14 '17 21:09 davidkpiano

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!

smattt avatar Feb 12 '18 23:02 smattt