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

How to prevent form resetting on failed submit #829

Open sonarforte opened 7 years ago

sonarforte commented 7 years ago

This is more of a general question, but it seems that when my form is submitted and the field-level validation fails, it resets all the fields (even those that did not fail) to their default values of empty.

This might be similar, but not related to the issue spoken about relating to this example, where components are set to an error state after failed submission (even if that component is not in error).

Just type in one of the fields, hit submit, and you'll see that both fields are in error.

Unfortunately, I am unable to modify the above example to show the form resetting on failed submission. I have no idea how to explain this, as all the functional code is the same on my local project, but I think this issue might be general enough that there is a general method of accomplishing this that I have been unable to find in the docs.

davidkpiano commented 7 years ago

The issue is that no input-specific props (onChange, onBlur, onFocus, etc.) were being passed into the actual input. Here's how you can refactor:

const UIInput = (props) => {
+  const { error, label, ...inputProps } = props;

  return (
    <div>
      <label>{error ? "Error" : label}</label>
+    <input type="text" {...inputProps}></input>
    </div>
  )
}
sonarforte commented 7 years ago

Thanks for the tip, it certainly gives the desired behavior on the codepen example.

However, and I hate to keep going on about this, I have what is essentially the exact same code in my component and it doesn't work. The errors update correctly, but whenever there is a submit (failed or successful), the fields get cleared.

I've analyzed the rendered html and there seems to be no difference in the html tags or attributes on the form components. The only difference (aside from the behavior) that I've observed is on my machine there is a prevalence of rrf/batch actions, which I do not see on the codepen example.

I'm really tearing my hair out as to why the same code works differently in two separate places, so I'm including (what I believe to be) all relevant files.

Thanks again for the help.

//package.json

{
  "name": "widget",
  "version": "0.1.0",
  "private": true,
  "devDependencies": {
    "react-scripts": "0.9.0",
    "redux-devtools-extension": "^2.13.2"
  },
  "dependencies": {
    "babel-polyfill": "^6.23.0",
    "classnames": "^2.2.5",
    "crypto": "0.0.3",
    "dns": "^0.2.2",
    "isomorphic-fetch": "^2.2.1",
    "jsonwebtoken": "^7.4.1",
    "jws": "^3.1.4",
    "jwt-simple": "^0.5.1",
    "prop-types": "^15.5.10",
    "react": "^15.5.4",
    "react-dom": "^15.5.4",
    "react-redux": "^5.0.5",
    "react-redux-form": "^1.12.0",
    "react-router": "^3.0.5",
    "react-router-redux": "^4.0.8",
    "redux": "^3.6.0",
    "redux-logger": "^2.10.2",
    "redux-saga": "^0.14.8",
    "semantic-ui-css": "^2.2.10",
    "semantic-ui-react": "^0.67.2",
    "validator": "^7.0.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}
// WidgetForm.js

import React, { Component } from 'react'
import { PropTypes } from 'prop-types'
import { connect } from 'react-redux'
import { Control, Form, } from 'react-redux-form'

import { widgetRequest, } from '../actions/'

//Same definition as codepen example
const UIInput = (props) => {
  const { error, label, ...inputProps } = props;
  console.log("UIINPUT Rendered")
  console.log(inputProps)

  return (
    <div>
      <label>{error ? "Error" : label}</label>
      <input type="text" {...inputProps}></input>
    </div>
  )
}

class WidgetForm extends Component {

  handleSubmit = (values) => {
    const { client, widgetRequest } = this.props
    console.log(values)
    widgetRequest(values)
  }

  render() {

    // Whether the field is empty
    const isRequired = (val) => val && val.length > 0
    // Whether to show the error message
    const requiredError = (props) => {
      const { touched, valid, focus } = props.fieldValue
      return !focus && touched && !valid
    }

    return (
      <Form
        model="forms.widget"
        onSubmit={(values) => this.handleSubmit(values)}
      >
        <Control.text
          model=".name"
          validators={{
            isRequired,
          }}
          component={UIInput}
          mapProps={{
            label: "Name",
            error: requiredError,
          }}
        />
        <Control.text
          model=".number"
          validators={{
            isRequired,
          }}
          component={UIInput}
          mapProps={{
            label: "Number",
            error: requiredError,
          }}
        />
        <Control.button
          model="."
          type="submit"
        >
          Submit
        </Control.button>

      </Form>
    )
  }
}

const mapStateToProps = (state, ownProps) => {

  const { requesting, successful,  } = state.forms.widget
  return {
    requesting,
    successful,
  }
}
export default connect(mapStateToProps, { widgetRequest })(WidgetForm)
// reducers/index.js

import { combineReducers } from 'redux';
import { combineForms } from 'react-redux-form';
import { syncHistoryWithStore, routerReducer } from 'react-router-redux'

// There are more things in real life but unrelated here
import { fooReducer as foo } from './foo'
import { widgetReducer as widget } from './widget'

const rootReducer = combineReducers({
  foo: foo,
  forms: combineForms({
    widget: widget,
  }, 'forms'),
  routing: routerReducer,
});

export default rootReducer;

Let me know if there's something I missed. Thanks again.