I'm using redux-form. I'm showing initial values in input fields from the state.
When I click on reset, the input field is still showing initial values.
How can I reset the input field?
This is my code :
const mapStateToProps = (state) => {
return {
initialValues: {
name:state.userInfo.data.name,
email:state.userInfo.data.email,
phone:state.userInfo.data.phone,
city:state.userInfo.data.city.name,
}
};
}
This is how I'm calling initial value in the input field.
const renderField = ({ input, label, type, meta: { touched, error } }) => (
<div>
<input className="form-control control_new" {...input} placeholder={label} type={type}/>
{touched && ((error && <span>{error}</span>))}
</div>
)
<Field type="text" {...name} name="name" component={renderField} label="Enter Your Name" />
Thanks
import {reset} from 'redux-form';
...
dispatch(reset('myForm')); // requires form name
Harsha's answer is correct, but you can also call this.props.reset(), which already knows your form name, from inside your decorated form component.
You can see it in action on the "Clear Values" button in the Simple Example.
TLDR: set enableReinitialize: true on your form
I had this same problem recently and came across this post, with none of the answers working. Finally found the solution so hopefully this will help someone else. the problem is those initial values not getting reset when you reset the form. here is a github issue asking about it, and here is the current documentation about it
Upon onClick of the clear button call a different function as follows.
onClick={this.onClearChanges}
Pass the initialValues to the function this way, and as everyone mentioned, Don't forget to set enableReinitialize: true.
onClearChanges = () => {
const { reset, onClearChanges, InitialValues } = this.props;
reset();
onClearChanges(InitialValues);
};
It's also possible to reset inside of your constructor if you don't have a button with a method to call.
I'm using a Drawer Navigator to navigate to different screens(not a clickable button with an onClick method) so I had to reset
constructor(props) {
super(props);
this.props.reset(); //<-- this
}
inside of the first component to handle that specific redux form and then I set
enableReinitialize: true
for that form.
Related
export default function Form() {
const [user, setUser] = useState({
name: "",
numOfQs: 0
})
console.log(user)
function handleUserDataChange(event) {
setUser(prevUser => {
return {
...prevUser,
[event.target.name]: event.target.value
}
})
}
return (
<>
<input
type="text"
placeholder="username"
name="name"
value={user.name}
onChange={handleUserDataChange} />
<input
type="number"
name="numOfQs"
value={user.numOfQs}
onChange={handleUserDataChange} />
</>
)}
I was trying to build my form using react, and when I tried to use input[type: number] on the form field it was giving me this error, don't know why. I was reading through react docs about forms, and everything from the checkbox, radio buttons, textarea was all working fine. but when I used an input element of the type number, I got the following error.
*!Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property target on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist(). See fb.me/react-event-pooling for more information.
so, the problem only arises when an input of type "number" is introduced. when I remove it all of my other form elements work fine.
I'm still in the learning phase of react. please help me out.
This happened because the event that passed into the function is used as an asynchronous event.
To fix this, decompose the event object
function handleUserDataChange(event) {
const { name, value } = event.target;
setUser(prevUser => {
return {
...prevUser,
[name]: value
}
})
}
I have a warning that says
Warning: You provided a `value` prop to a form field without an `onChange` handler.
This will render a read-only field. If the field should be mutable use `defaultValue`.
`Otherwise, set either `onChange` or `readOnly`.`
Of course, I have googled a solution for it. One way to fix it is to replace "value" attribute with "defaultValues". But, this is causing another issue because my component is a controlled.
I could add onchange attribute and method to the fields. But the problem is that it will cause redundancy as I have a single onChange method on the tag that manages all the values and states. For example,
const [allStates, setAllStates] = useState({value_one: "", value_two:""});
function handleOnChange(event){
.........
//update the allStates state here
}
<Form onChange={handleOnChange}>
<input value={allStates.value_one} />
<input value={allStates.value_two} />
</Form>
What are the alternatives I have? Should I just ignore it since I can only see it on dev environments, the end users cannot and won't see it on the prod environment anyway?
It is a Helpful Warning by React, you can probably refer to this thread which discusses this.
https://github.com/facebook/react/issues/1118
Although adding the defaultValue should work for you, since your state has the latest value for the inputs, and when your component would render for the first time as long as the defaultValue is assigned to your <input>, the fields would reflect your state.
const [state, setState] = useState({});
const handleChange = (ev) => {
setState(prev_state => {
return {
...prev_state,
[ev.target.name]: ev.target.value
}
})
}
<form onChange={handleChange}>
<input name={"foo"} defaultValue={state.foo}/>
<input name={"bar"} defaultValue={state.bar}/>
</form>
The warning it is giving you is that you have provided the input with a value argument, so when you go to type in the input, it won't work:
const [allStates, setAllStates] = useState({
value_one: 'I will be display in the input initially', // <-- This text will already be entered in the text input.
value_two: ''
});
<input value={allStates.value_one} />
But since you added the value prop, you need to give it an onChange, otherwise the value of the field will always be the same and you won't be able to change it.
Ofcourse you can add the onChange like this:
const [allStates, setAllStates] = useState({
value_one: '',
value_two: ''
});
const handleOnChange = (event) => {
setAllStates({
...allStates,
// Now you can target the name prop
[event.target.name]: event.target.value
});
}
return (
<>
{/* I will suggest adding a name prop also */}
<input name="value_one" value={allStates.value_one} onChange={handleOnChange} />
<input name="value_two" value={allStates.value_two} onChange={handleOnChange} />
</>
);
The name prop is really useful for these one function setups, as it allows you to target the state value straight from the name.
But remember that the name values should be the same as the values in the state.
I have a form (let's call it the LoginContainer) that has the <Field /> component on it:
import { reduxForm, Field } from 'redux-form'
...
class LoginContainer extends Component {
...
render() {
const { handleSubmit, pristine, submitting } = this.props
return (
...
<Field
component={AuthFormInput}
type="password"
name="person_password"
placeholder="Password"
/>
AuthFormInput.jsx
const AuthFormInput = ({
input,
name,
placeholder,
type,
meta: { touched, error },
}) => {
return [
<div className="row-md" key={`${name}_field`}>
<input
placeholder={placeholder}
name={name}
type={type}
className={maybeHasError}
{...input}
/>
</div>, // error display works amazing here, no problem
<div key={`${name}_error`}>{touched && (error && <span className="edit_required">{error}</span>)}</div>,
]
}
The problem is, I am trying to handle errors outside of the AuthFormInput component, and I can't find any solution that allows me to access the touched state of each field from anywhere other than AuthFormInput, which is abstracted inside <Field />
Is there a way I can get access to meta.touched from inside LoginContainer?
Redux-Form doesn't seem to have any kind of getTouchedFields() function that I can use.
I can collect the currently errored fields easily through the validate function, but I can't find a way to see what fields are touched.
I can't find a way to pass data from AuthFormInput to LoginContainer because <Field /> is between them which is internally controlled by Redux-Form.
Is there any way to do this with Redux-Form V7?
Is there a way I can create a HOC that allows me to track meta.touched inside <Field />?
Ok, I was able to achieve this and it was extremely non-trivial.
Stepping forward from the code in the above question, I added this to LoginContainer:
import withFormListener from './withFormListener'
const listeningAuthFormInput = withFormListener(AuthFormInput)
So, then <Field /> became this:
<Field
component={listeningAuthFormInput}
type="password"
name="person_password"
placeholder="Password"
/>
The HOC wraps around the text input field (or any field you want), and simply listens for changes to the props and then updates Redux:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { updateField } from './auth_actions'
const withFormListener = function listenToFormField(FormField) {
class WrappedField extends Component {
handleFieldUpdate() {
const { name } = this.props.input
const { touched, error } = this.props.meta
if (touched && error) {
if (this.props[`error_${name}`] === error) return null
return this.props.updateField({ prop: name, value: error })
}
if ((touched && !error) && this.props[`error_${name}`].length) {
return this.props.updateField({ prop: name, value: '' })
}
return null
}
componentDidUpdate() {
this.handleFieldUpdate()
}
render() {
return <FormField {...this.props} />
}
}
const mapStateToProps = ({
auth: { error_person_tel, error_person_password },
}) => ({
error_person_tel, error_person_password,
})
return connect(mapStateToProps, { updateField })(WrappedField)
}
export default withFormListener
Logically, what is happening in the HOC is:
this.props inside AuthFormInput becomes this.props inside the HOC
listen to this.props.meta and this.props.input
if the field is touched and has an error, update Redux
if the field is touched and no longer has an error, wipe it out of Redux
You should see my action creator and reducer from LoginContainer so there is no confusion.
Action Creator
export const updateField = ({ prop, value }) => ({
type: AUTH_UPDATE_FIELD,
payload: { prop, value },
})
Reducer
I recommend keeping the state object flat. I experimented with nesting in here, such as:
const INITIAL_STATE = {
errors: { field1, field2 }
}
But, it was giving me significant problems with updating the state, due to equality reasons and complexity with avoiding overwriting errors. It's much nastier than it looks. Therefore, I have settled on this:
const INITIAL_STATE = {
...
error_person_tel: '',
error_person_password: '',
}
case AUTH_UPDATE_FIELD: {
const { prop, value } = action.payload
return {
...state,
[`error_${prop}`]: value,
}
}
You can read more about normalizing state here (ie: flattening):
https://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
I haven't got around to optimizing it at all, but here is how I am displaying the errors currently (inside LoginContainer):
<ServerErrors
errors={[
this.props.error_person_tel,
this.props.error_person_password,
].filter(val => val)}
/>
It is a simple component that renders either null or an array of error strings.
I highly recommend using the built-in Redux-Form field error handling. It works great. I unfortunately had to abstract the control away from the field-level to accomplish a UI design, so this was the only solution in V7.
I searched for weeks. I made another Stack Overflow question and it got no activity, neither did this one. I'm glad I was able to achieve a functioning solution.
I also highly recommend you take a look at Formik. I used it in React Native to accomplish this UI requirement a few weeks ago. You will likely find it works very well. Talk to Jared Palmer on Twitter if you encounter any issues with it. Formik boasts performance gains vs. Redux-Form due to smarter architecture. I am very happy with it in React Native.
If you venture down this road, be extremely careful about setting off infinite loops caused by setState. I did hours and hours and hours of research and trial & error to make this :) I am reasonably happy with the conciseness of the solution. Please let me know if you have a better way.
I have given someone else's React project which is using the React-Redux-Form (this is not the redux-form) and there is a need to enable a checkbox which is on another React Component based upon a value being entered in a textbox in the React-Redux-Form. By defult the checkbox is disabled and should only be enabled if a value is present.
Searching on the Internet have failed to find an example in which to use. Can anyone help?
Here is a very rough example, and is not intended to run - simply show the flow of how to do it:
The component with the input field might have something like:
//1) this is setting a local state, when the user submits you can send it to redux so that it is available across the app.
this._saveInputToLocal() {
var emailRegEx = /^.+#.+\..+$/i;
this.setState({
email: e.target.value
}, function() {
}.bind(this));
},
//2) when the user clicks submit - send the input to redux
//lets assume this is saved into a property on state called `formInput`:
this._saveInputToRedux() {
const = { dispatch }
dispatch(saveUserInput(this.state.email))
},
render: function(){
return (
<div className={`field email ${css(styles.emailContainer)}`}>
<input
type='email'
placeholder={ 'helloworld#code.com' }
onChange={this._saveEmailToState}
/>
</div>
<button type='submit' onClick={this._saveInputToRedux}>Submit</button>
)
}
});
So you see, you have a few things: a function that updates a local state, and one that handles the submit - where the value of the local state fires and action that will store that value in redux. Don't forget to import connect so that you have dispatch available to you on props, and also don't forget to import your action into the component.
Now, In the other component that has the checkbox:
// 3) assuming you have mapped your redux state to your props you make a function that checks for a valid input value:
this._hasValidValue() {
const { formInput } = this.props
return formInput && formInput !== null && formInput !== undefined && formInput.length > 0
}
//here in the render you display either an enabled or disabled checkbox based on if you have a valid input from the other component. Because this is based on state, this component will re-render when state is updated (goes from invalid to valid or vice versa)
render: function(){
return (
<div className="checkbox">
{ hasValidValue()
? <input type="checkbox" name="some-name" />
: <input type="checkbox" name="some-name" disabled/>
}
)
}
});
I'm working on a page which has many input validations and logical bindings on it and every sprint iteration the page size increasing. So that, I have to find a beautiful and scalable solution.
Imagine, when user select a value from dropdown as 'A', some fields must be disabled, some fields must be cleared and some fields initilized with default values. I can change one related field (doesn't have validation rule like regexp or lenght constrait) value with some little code like
this.props.dispatch(change('xForm','xField','xValue' ))
My problem is that when I need to clear multiple fields,
It always blocked by my validation method and clear operation is failed ( Note : I supposed to be like that but not like that)
.
I tried some strategies as below but y,z,w fields have some text and it triggered validation rule and hanled errors. So that, inputs have still old values, not cleared ones.
//Clear
this.props.dispatch(change('xForm','yField','' ))
this.props.dispatch(change('xForm','zField','' ))
this.props.dispatch(change('xForm','wField','' ))
What are the best practises for clear inputs or assign some values to inputs in redux-form for pages which have highly dependent inputs.
I have been researching for 2 days but I couldn't find any optimal solution. (redux normalizer, redux form utils etc.)
Thanks.
This worked for me:
resetAdvancedFilters(){
const fields = ['my','fields','do','reset','properly']
for (var i = 0; i < fields.length; i++) {
this.props.dispatch(change('formName',fields[i],null))
this.props.dispatch(untouch('formName',fields[i]))
}
}
Using the below, it clears the respective multiple fields and also clears the errors if any for those respective fields.
Common function to reset multiple fields.
import {change, untouch} from 'redux-form';
//reset the respective fields's value with the error if any
resetFields = (formName, fieldsObj) => {
Object.keys(fieldsObj).forEach(fieldKey => {
//reset the field's value
this.props.dispatch(change(formName, fieldKey, fieldsObj[fieldKey]));
//reset the field's error
this.props.dispatch(untouch(formName, fieldKey));
});
}
Use the above common function as,
this.resetFields('userDetails', {
firstName: '',
lastName: '',
dateOfBirth: ''
});
Wow, that's some complicated logic. The absolute ideal way would be to use the plugin() API to change values in the reducer when your other field values change. It's a complicated API, because you have total control to mess things up in the reducer, but you have a complicated requirement.
However, dispatching three change() actions like you said you are doing should also work fine. You might want/need to untouch() the fields as well if they have Required validation messages that you don't want to show yet.
It always blocked by my validation method and clear operation is failed.
Can you elaborate on what that means, show a stack trace or console error output?
there.
Let's look at http://redux-form.com/6.0.0-alpha.4/docs/faq/HowToClear.md/
For whole form we can use 4 methods:
A) You can use the plugin() API to teach the redux-form reducer to respond to the action dispatched when your submission succeeds.
B) Simply unmount your form component
C) You can call this.props.resetForm() from inside your form after your submission succeeds.
D) You can dispatch reset() from any connected component
Try to use the dispatch function in meta object with following payload:
meta.dispatch({
type: '##redux-form/CHANGE',
payload: null,
meta: { ...meta, field: name },
})
That should do the trick for any field you want.
I found a better way for this,
We can use initialize action creator of RF.
let resetFields = {
firstName: '',
lastName: '',
dateOfBirth: ''
}
this.props.initialize(resetFields, true); //keepDirty = true;
If the keepDirty parameter is true, the values of currently dirty fields will be retained to avoid overwriting user edits.
clear multiple fields
import {change} from 'redux-form';
const fields = {name: '', address: ''};
const formName = 'myform';
const resetForm = (fields, form) => {
Object.keys(fields).forEach(field => dispatch(change(form, field, fields[field])));
}
resetForm(fields,formName);
If we are talking about entering fields into a form, and then navigating away before submitting, this is the best solution. Place this in your componentDidMount() with the fields you want cleared.
this.props.initialize({
firstName: null,
lastName: null,
bankRoutingNum: null,
newBankType: null
});
For a functional component, you can you change function of RF.
Pass change as props to your component and use it like change(Field_name, value)
For example:
import { reduxForm } from "redux-form";
const Screen = ({
handleSubmit,
change
}) => {
onChange = () => {
change(Field_name, value)
}
return (
// Your code
)}
You can use the clearFields prop or action creator to clear multiple fields.
For example, in this simple form, I use the clearFields prop to clear the firstName and lastName fields.
import React from "react";
import { Field, reduxForm } from "redux-form";
const SimpleForm = (props) => {
const { clearFields } = props;
const clearName = (e) => {
e.preventDefault();
clearFields(false, false, ...["firstName", "lastName"]);
};
return (
<form>
<div>
<label>First Name</label>
<Field name="firstName" component="input" type="text" />
</div>
<div>
<label>Last Name</label>
<Field name="lastName" component="input" type="text" />
</div>
<div>
<label>Occupation</label>
<Field name="occupation" component="input" type="text" />
</div>
<button onClick={clearName}>Clear Name</button>
</form>
);
};
export default reduxForm({
form: "simple"
})(SimpleForm);