How to set initialValue with semantic-ui-redux-form-fields - redux-form

I'm trying to create an Edit form w/ semantic-ui-redux-form-fields. The forms work fine w/ a blank starting point, but for an edit form where I have initial values, I haven't been able to get it to work.
If I'm using straight redux-form, this works fine for initializing the form as long as I have a prop of initialValues passed into my form component.
Using that logic, I would think to all I would need to do is change the Field#component attribute from "input" to {Input} (/w the appropriate import). Note that I never see currentValue being passed in via props. It's not clear how this prop would ever get populated.
Props:
{initialValues: {"first_name": "Bob"}}
Working Component:
import { Form, Button } from 'semantic-ui-react'
import { reduxForm, Field} from 'redux-form'
...
return <Field component="input" type="text" name='first_name'/>
Not Working Component:
import { Form, Button } from 'semantic-ui-react'
import { reduxForm, Field} from 'redux-form'
import { Input } from 'semantic-ui-redux-form-fields'
...
return <Field component={Input} type="text" name='first_name'
currentValue={this.props.currentValue}/>

The Input component in semantic-ui-react has a defaultValue prop that I believe you should be able to use to initialize the Field with data since you are passing it the Input in it's component prop.
<Field
component={Input}
defaultValue={this.props.currentValue}
name='first_name'
type='text'
/>

Related

Redux-form validation breaks when using multiple components with the same form name

I run into the validation issue using multiple components decorated with same form name.
Let's say we have SimpleForm1 and SimpleForm2. When rendering Only SimpleForm1 with the name field validation works as expected, as well as when rendering SimpleForm2 with the surname field. But when rendering them both on a single page validation for SimpleForm1 is broken.
The question is how to avoid such behaviour and make both validation functions work.
Here is a fiddle which illustrates my problem
It's not a good idea to use same names for multiple forms.
As i understand you need to dynamically add form inputs(SimpleForm2 in your example) and have possibility to submit both forms with one button.
If yes, so you can add just an input to first form, you don't need second form.
Form:
const SimpleFormComponent1 = props => {
const {handleSubmit, pristine, reset, submitting, renderBoth} = props;
const onSubmit = (values) => {
alert(JSON.stringify(values));
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
...
{
renderBoth &&
<Field
name="surname"
type="text"
component={renderField}
label="Surname"
validate={validateSurname}
/>
}
...
</form>
)
};
Render function:
render() {
const {renderBoth} = this.state;
return (
<div>
<div className='forms'>
<SimpleForm renderBoth={renderBoth}/>
</div>
<button
type='button'
onClick={this.handleClick}>
{renderBoth ? 'Show Single' : 'Show Both'}
</button>
</div>
);
}
Updated example
SOLUTION 1
I was having interference with invalid and valid props of the Redux Form because I wrote the validate function on ReduxForm() in every file of my main form component.
You haven't to change the form name to solve this. You have to put the validation function on the parent component.
Here is an example:
[CONTEXT]
I have 3 Components:
1. Main component to put the form ('editUserForm') elements (, , ...)
2. Field1 of the form ('editUserForm') that changes user's complete name.
3. Field2 of the form ('editUserForm') that changes user's email.
[SOLUTION]
MAIN COMPONENT:
Inside the main frame, you call reduxForm() (it creates a decorator with which you use redux-form to connect your form component to Redux. More info here Redux Form docs). Your code would be like this:
import...
class MainFrame ... {
...
<form ...>
<Field1 />
<Field2 />
</form>
...
}
const validate ({ name, email }, props) => {
errors={}
// Validation around Field1
if (name === ...) errors.name = "Name error passed to Field1 component";
// Validation around Field2
if (email === ...) errors.email= "Email error passed to Field2 component";
return errors;
}
...
export default reduxForm({
form: 'editUserForm',
validate // <--------- IMPORTANT: Only put this function on the parent component.
})(MainComponent);
FIELD1 & FIELD2 COMPONENTS:
This code is for the 2 children components. IMPORTANT: You call reduxForm() without validate Redux Form docs synchronous function.
import...
class MainFrame ... {
...
const { invalid } = this.props;
...
<inputs
error={invalid}
>
...
</input>
...
}
// IMPORTANT: Don't put the validation function in Field1 and Field2 components, because their local validations will interfere with the validation of the other Field component.
reduxForm({
form: 'editUserForm'
})
Now, the props: valid and invalid will work perfectly inside the children components (Field1 and Field2).
SOLUTION 2
User Redux Form FormSection (docs) to split forms into smaller components that are reusable across multiple forms.

What is the good way to have a multi input field in Redux Form?

In my project we are building a form with React and Redux-Form. We have a single information that is composed by the value of two inputs. But the values of each input is combined and validated together.
The first implementation try was by connecting each component with Field. It let us update the state properly but we couldn't validate all the values together with the validate prop.
The second Try was using Fieldscomponent but it does not have a validate prop. We thought in create a Pull Request for it but the API for it isn't clear yet, since what we want to validate the combination of the two values and the behavior of the Fields props (such as parse and format) is different, executing the function for each input inside Fields component separately.
I know it is possible to create a component and use Field to connect with the application state, but I didn't want to manage things as the touched prop, or the callbacks to update the state, or other things that I even have noticed, since Redux-Form has all of it done.
The fact is that I end up with an implementation but it didn't looked very elegant. I'd like you to take a look at the implementation and give your opinion, sugest other solutions and even if this solution is not implemented in Redux-Form yet we could maybe open a pull request for that.
Here is an example implementation
Simple form container
import SimpleForm from 'app/simpleForm/components/simpleForm'
import { reduxForm } from 'redux-form'
export default reduxForm({
form: 'simpleForm'
})(SimpleForm)
Simple form component
import React from 'react'
import { Field } from 'redux-form'
import MultiInputText from 'app/simpleForm/components/multiInputText'
const onSubmit = values => alert(JSON.stringify(values))
const validateAddress = (value) => {
if (!value) return 'Address is empty'
if (!value.street) return 'Street is empty'
if (!value.number) return 'Number is empty'
return null
}
const SimpleForm = ({ handleSubmit }) => {
return (
<form onSubmit={ handleSubmit(onSubmit) }>
<Field label="Home Address" name="home" component={MultiInputText} type="text" validate={validateAddress}/>
<Field label="Work Address" name="work" component={MultiInputText} type="text" validate={validateAddress}/>
<button type="submit">Submit</button>
</form>
)
}
export default SimpleForm
MultiInputText component
import React from 'react'
import { Fields, FormSection } from 'redux-form'
const renderAddreessInputs = ({ street, number, error }) => (<div>
<input {...street.input} type="text" />
<input {...number.input} type="text" />
{ street.meta.touched && number.meta.touched && error && <span className="error">{error}</span> }
</div>)
const MultiInputText = (props) => {
const { input: { name }, label, meta: { error }} = props
const names = [
'street',
'number'
]
return (<div>
<label htmlFor={name}>{label}</label>
<FormSection name={name}>
<Fields names={names} component={renderAddreessInputs} error={error}/>
</FormSection>
</div>)
}
export default MultiInputText
I see two options:
1) Use record-level validation.
reduxForm({
form: 'addressForm',
validate: values => {
const errors = {}
if(!home) {
errors.home = 'Address is empty'
}
// etc, etc. Could reuse same code for home and work
return errors
}
})
2) Create a single input that handles a complex value.
<Field name="home" component="AddressInput"/>
...
const AddressInput = ({ input, meta }) =>
<div>
<input
name={`${input.name}.street`}
value={(input.value && input.value.street) || ''}
onChange={event => input.onChange({
...input.value,
street: event.target.value
})}/>
...other inputs here...
</div>
That's total pseudocode, but I hope it gets the point across: a single input can edit a whole object structure.
Personally, I'd choose Option 1, but I prefer record-level validation over field-level validation in general. The nice thing about Option 2 is that a single AddressInput could be reused across the application. The downside is that you don't get specific field-level focus/blur/dirty/pristine state.
Hope that helps...?

Update a value on redux form

I have a react-bootstrap class of a form. The code is something like that:
class MyReactClass extends Component {
// ....
render() {
return (
<div>
<Field name="combobox1" component={ComboBox} options={options} ></Field>
<Field name="textField2" component={My_TextField}></Field>
<Field name="textField3" component={My_TextField}></Field>
<Field name="textField4" component={My_TextField}></Field>
</div>
}
}
export default reduxForm({
form: 'myReactClass ',
}
)(MyReactClass );
I added an initialization to initialize some values:
componentDidMount() {
this.props.initialize({textField2:'blabla'});
}
Now I want that when the user selects a value from the combobox, it'll automatically change the value of textfield3. I have a WORKING onChange function for the combobox, but I can't find a way to change textfield3's value. I tried to use "initialize" again -
this.props.initialize({textField3: this.getUpdatedValue()});
but it initialize the whole form and the form gets cleared and have only the value for textfield3.
I tried to access this.props.textField3 but I got undefined.
How can I update the form's value?
this.props.change('textField3', this.getUpdatedValue()) would work.
Optionally, if you need the user to understand that the value was autofilled but can be manually changed, you can use:
this.props.autofill('textField3', this.getUpdatedValue())
These are described in the docs here.

How to manually trigger onChange event without <Field> element?

I have component that renders like this:
<MyComponent
prop1Value={1}
prop1OnChange={this.handleProp1Change}
prop2Value={2}
prop2OnChange={this.handleProp2Change}
/>
So it contains two fields/values internally. How I can sync it with redux-form? <Field> component provides only one value/onChange pair...
Tried it like this:
// values comes from getFormValues(...)
<MyComponent
prop1Value={values.prop1}
prop1OnChange={this.context._reduxForm.change.bind(this.context._reduxForm, 'prop1')}
prop2Value={values.prop2}
prop2OnChange={this.context._reduxForm.change.bind(this.context._reduxForm, 'prop1')}
/>
You'll have to use connect to bind those actions.
import { change } from 'redux-form';
import { connect } from 'react-redux';
const ParentComponent = (props) => {
return <div>
<MyComponent propOneOnChange={props.change('formName', 'fieldName', 'fieldValue')} />
</div>
}
export default connect(mapStateToProps, { change })(ParentComponent);

redux form, Field component issues

I have some issues using "Field" component in Redux Form in my code. Just follow the simple example on redux-form website.
/* MyForm.jsx */
...
import { Field, reduxForm } from 'redux-form';
class MyForm extends Component {
...
<form onSubmit={handleSubmit(...)}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" placeholder="First Name"/>
</div>
</div>
...
}
export default reduxForm({
form: 'myForm'
})(MyForm);
Here is the parent component that use the redux form.
/* page.jsx */
import MyForm from './MyForm';
...
<MyForm/>
...
The form does not render and in console the error says:
React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components).
Uncaught Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
However, if I use material-ui or bootstrap form field, it works fine.
I mounted the form reducer before render the form:
import {reducer as formReducer} from 'redux-form';
const myReducer = combineReducers({
...
form: formReducer
});
Any ideas why Fields component does not work?
Thanks,
Try upgrading your redux to 6.0. Field component started at v6.0, I believe.

Resources