react-hook-form V7 passing register props to custom field - javascript

I want to create a custom input with new register method by passing into props of react-hook-form, I have the same message everytime:
TypeError: path.split is not a function
https://gyazo.com/414ea28dbe2b016e5b0739660efdc84b
My custom input
function Field({
name,
register,
placeholder,
type,
value,
onChange,
defaultValue,
errors,
children
}){
return(
<Form.Group>
<Form.Label htmlFor={name}>{children}</Form.Label>
<Form.Control
size="lg"
placeholder={placeholder}
type={type ? type : "text"}
id={name}
// name={name}
value={value}
onChange={onChange}
{...register({name})}
defaultValue={defaultValue}
/>
{errors && <span className="text-danger">{errors.message}</span>}
</Form.Group>
);
}
And my form part where I call my custom input:
const {register, formState : { errors }, handleSubmit} = useForm({
mode: "onTouched",
resolver: yupResolver(schema),
});
...
<Field
name="login"
register={register}
errors={errors.login}
>
...

From the migration guide to v7:
Custom register
You will no longer need the name attribute for custom
register, you can supply the name of the input straight way.
- register({ name: 'test' })
+ register('test')
It looks like react-hook-form is trying to split the name string in case you have a nested field:
'test.0.nested'.split('.')
But with the new version you should be supply a string instead of an object with a name property so:
{ name: 'test.0.nested' }.split('.')
Throws the error.

Related

Set the value of a form based on state in React

Using Formik for validating some fields, if a state value is true I want that the value of a fields to be copied to the value of another one.
For example, value of mainAddress to be copied to address.
There is a state variable, setAddress which is set on false but it can be changed to true when a checkbox is clicked.
When this variable is set on true I want that the value of mainAddress to be copied to address
This is the code that works fine without copying that value:
import React from 'react';
import { Formik, Form, Field } from 'formik';
import { Input, Button, Label, Grid } from 'semantic-ui-react';
import * as Yup from 'yup';
import { Creators } from '../../../actions';
import './CreateCompanyForm.scss';
class CreateCompanyForm extends React.PureComponent {
constructor(props) {
super(props);
this.state = { // state variables
address: '',
mainAddress: '',
setAddress: false,
};
}
handleSubmit = values => {
// create company
};
toggleAddress = () => { // it toggles the value of setAddress
this.setState(prevState => ({
setAddress: !prevState.setAddress,
}));
};
render() {
const initialValues = { // the address and mainAddress are set to '' at the start
address: '',
mainAddress: '',
};
const validationSchema = Yup.object({
address: Yup.string().required('error'),
mainAddress: Yup.string().required('error'),
});
return (
<>
<Button type="submit" form="amazing"> // button for submit
Create company
</Button>
<Formik
htmlFor="amazing"
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={values => this.handleSubmit(values)}>
{({ values, errors, touched, setValues, setFieldValue }) => (
<Form id="amazing">
<Grid>
<Grid.Column> // mainAddress value
<Label>Main Address</Label>
<Field name="mainAddress" as={Input} />
</Grid.Column>
<Grid.Column> // address value
<Label>Address</Label>
<Field name="address" as={Input} />
<div>
{this.state.setAddress // here is how I've tried to set that value
? values.address.setFieldValue(values.mainAddress)
: console.log('nope')}
{touched.address && errors.address ? errors.address : null}
</div>
</Grid.Column>
</Grid>
<Checkbox
label="Use same address"
onClick={() => this.toggleAddress()}
/>
</Form>
)}
</Formik>
</>
);
}
}
So I've tried more ways to solve it but without success. Now in the code it is:
{this.state.setAddress
? values.address.setFieldValue(values.mainAddress)
: console.log('nope')}
Other one was: (this.state.setAddress ? values.address = values.mainAddress)
None of them work. Is it possible to get the value from values.mainAddress and copy it to values.address? Or to its input?
You can rewrite the Field component for address accordingly.
<Field name="address">
{({
field, // { name, value, onChange, onBlur }
}) => {
values.address = values.mainAddress;
return (
<div>
<input
{...field}
type="text"
placeholder="Address"
/>
</div>
);
}}
</Field>
Here we are setting the value of address field to values.mainAddress if setAdress checkbox is checked else we let formik value to fill it.
Here is the working codesandbox code - https://codesandbox.io/s/fervent-tereshkova-sro93?file=/index.js
Formik also provides values object you could use that for updating address value same as mainAddress. Just provide name attribute to both input fields then assign like this - props.values.address = {...props.values.mainAddress}

Passing a 'label' prop into Formik <Field /> when using alongside Material UI <TextField />

I'm building a form using Formik and Material UI.
I'm leveraging the Formik component the following way :
My Input component :
const Input = ({ field, form: { errors } }) => {
const errorMessage = getIn(errors, field.name);
return <TextField {...field} />;
};
And down into my rendered form, here's how I do it :
<Field
component={Input}
name={`patients[${index}].firstName`}
/>
The problem is that Material UI uses a label prop to display label on an input field so the label should be a prop passed to .
It works if I "hard-code" it into my "Input" component which defeats the purpose of using a reusable comp.
So that works but inconvenient :
const Input = ({ field, form: { errors } }) => {
console.log(field.label);
const errorMessage = getIn(errors, field.name);
return <TextField {...field} label="first name" />;
};
What I hoped for was using it one level above such as :
<Field
component={Input}
name={`patients[${index}].firstName`}
label="first name"
/>
but the above doesn't work as "label" is not recognised as a prop by Formik (or that's how I understand it but I might be wrong).
Has anyone come across that issue ?
I know I could use my "name" value as a label but it's not great UX as it would leave me with a label such as "patients[0].firstName" aha
Here is a good solution, that is essentially yours, but you can provide anything, not only label.
Create the field and put the label on the like so:
<Field name={`patients[${index}].firstName`} label='First Name' component={MuiTextFieldFormik} />
The trick is to use the spread operator, then the custom component becomes:
import React from 'react';
import { TextField } from "#material-ui/core"
import { get } from "lodash"
export const MuiTextFieldFormik = ({ field, form: { touched, errors }, ...props }) => {
const error = get(touched, field.name) && !!get(errors, field.name)
const helperText = get(touched, field.name) && get(errors, field.name)
return (
<TextField fullWidth variant="outlined" {...field} {...props} error={error} helperText={helperText} />
)
}
disappointing that their docs do not have such simple example
Ok so I think I found the solution. The way I was destructing my arguments, I was only passing field and form which are holding most of the data from so passing a label prop this way fixes that:
const Input = ({ field, label, form: { errors } }) => {
const errorMessage = getIn(errors, field.name);
return <TextField {...field} label={label} />;
};
Then when I use the Formik component this way, the correct label gets passed :
<Field
label="first name"
component={Input}
name={`patients[${index}].firstName`}
/>

How to edit a form in wizard and save its state for wizard

I am making a wizard in React js. I am already populating data in form using get api call. Now I want to edit the data in form, that retains it's state over the application flow.
Redux-Form allow you to pass custom props into Field so you can use this as a way to pass retrived value from your api into rendered component.
Base on your example link you can do this:
Modify renderField to accept custom prop value (or any other name you want) and pass it into input value.
const renderField = ({
input,
label,
type,
value,
meta: { touched, error }
}) => (
<div>
<label>{label}</label>
<div>
<input {...input} placeholder={label} type={type} value={input.value ?
input.value : value} />
{touched && error && <span>{error}</span>}
</div>
</div>
);
Define state variable and change handler:
const [email, setEmail] = useState("");
const handleChange = e => {
setEmail(e.target.value);
};
The initial value of your field (in this example 'Email') can be retrived from api as follow:
useEffect(() => {
/* your api call to fetch data */
fetch(....)
.then(res => setEmail(res.data));
}, []);
Then in your field set props and set onChange handler
<Field
name="email"
type="email"
component={renderField}
label="Email"
props={{ value: email }}
onChange={handleChange}
/>
Here is a working example: https://codesandbox.io/s/redux-form-wizard-example-7v3iy?file=/WizardFormSecondPage.js

Warning with React Bootstrap tooltip: Received `true` for a non-boolean attribute `show`

I'm getting mad from that. I would like to get rid of that warning:
index.js:1446 Warning: Received true for a non-boolean attribute
show.
If you want to write it to the DOM, pass a string instead: show="true"
or show={value.toString()}.
in div (created by Tooltip)
I'm making a validation form for registrating users. I show tooltip, when password validation fails - when passwords in two inputs are different.
I have attatchRefs in the constructor:
this.attachRefPass = targetPass => this.setState({ targetPass });
Next, between other begin values in constructor:
this.state = {
[...]
password: "",
password2: "",
showTooltipPass: false,
[...]
}
Validation method:
passwordValidation = () => {
this.setState({
showTooltipPass: this.state.password === this.state.password2
});
};
And the Form and Tooltip components:
<Form.Group as={Col} controlId="formGridUsername">
<Form.Label>Username</Form.Label>
<Form.Control
required
name="username"
placeholder="Username"
onChange={this.onChangeUsername}
ref={this.attachRefUser}
/>
<Overlay
target={targetUser}
show={showTooltipUser}
placement="right"
>
{props => (
<Tooltip id="overlay-example" {...props}>
There is one username like that already in the system!
</Tooltip>
)}
</Overlay>
</Form.Group>
The Tooltip is from react-boostrap:
https://react-bootstrap.github.io/components/overlays/
It is the bug that was fixed that Overlay outputs show value as Boolean
You may fix the issue localy with overriding show prop
{props => (
<Tooltip id="overlay-example" {...props} show={props.show.toString()}>
There is one username like that already in the system!
</Tooltip>
)}
You are setting a Boolean value (true or false) in state.showTooltipPass, and passing it to Overlay’s show prop, which appears to expect a string value (”true” or ”false”).
If you pass the value in as show={showTooltipPass.toString()}, this will convert the boolean to a string in the place you need it to be a string.

Material ui textfield ith redux form issue

i'm having an issue here which i can't handle
I have a textfield which i made compatible with redux form
like this:
const renderTextField = props => (
<TextField {...props} />
);
and i'm using it like this:
<Field
id="searchCif"
name="searchCif"
component={renderTextField}
floatingLabelText={SEARCHVIEW_HINT_CIF}
floatingLabelFixed={false}
value
/>
Then i writing this in my container:
import { reduxForm } from 'redux-form/immutable';
import { connect } from 'react-redux';
// import { injectIntl } from 'react-intl';
import SearchDefaultView from './views/searchDefaultView';
import { requestCustomerInfo } from './actions/customerActions';
export const mapDispatchToProps = dispatch => (
{
requestCustomerInfo: formData =>
dispatch(requestCustomerInfo(formData))
}
);
const SearchDefaultReduxForm = reduxForm({
form: 'customerInfo', // a unique identifier for this form
})(SearchDefaultView);
const SearchDefaultContainer = connect(
null,
mapDispatchToProps
)(SearchDefaultReduxForm);
export default SearchDefaultContainer;
But when i'm writing the value and submit my form the form has NO VALUES. what am i missing?
From the dicumentation i used this:
const renderTextField = ({
input,
label,
meta: { touched, error },
...custom
}) =>
<TextField
hintText={label}
floatingLabelText={label}
errorText={touched && error}
{...input}
{...custom}
/>
const SearchDefaultView = (props) => {
const { requestCustomerInfo, handleSubmit } = props;
return (
<form onSubmit={handleSubmit(requestCustomerInfo)}>
<Menu
autoWidth={false}
style={styleSearchMenu}
>
<Divider />
<Field
id="searchCif"
name="searchCif"
component={renderTextField}
floatingLabelText={SEARCHVIEW_HINT_CIF}
floatingLabelFixed={false}
/>
<br />
<Field
id="searchAFM"
name="searchAFM"
component={renderTextField}
floatingLabelText={SEARCHVIEW_HINT_AFM}
floatingLabelFixed={false}
/>
<br />
<RaisedButton
type="submit"
fullWidth
primary
label={SEARCHVIEW_SEARCH}
/>
</Menu>
</form>
);
};
But it is showing me an error in compilation at ...custom
When you want to use a custom field for Redux-Form, Redux-form gives you access to both props like onChange etc, but also other meta-data (like if the form has been touched or not). These different kinds of props are grouped depending on type. The interesting part for you is that all the attributes associated with a normal input element (like onChange, value, type) are grouped in props.input. So to pass those down to the <TextField /> component you can't use the spread operator (... ) on props directly. You must use it on props.input.
const renderTextField = props => (
<TextField {...props.input} />
);
You may also have to deal with the fact that the onChange method that <TextField /> expects doesn't necessarily have the same signature as the onChange method that Redux-form provides you. So you may have to do some manual work to make them work together, similar to what I've outlined in this post. You'd have to read up on the documentation of both the onChange of Redux-Form and Material-UI TextField respectively.
You may also be interested to know that for material-ui components, there actually already exists a library that has done that manual work for you: redux-form-material-ui.
I think you are not using the onChange prop of the component.
onChange: Callback function that is fired when the textfield's value changes.
You should dispatch the change and update the data in redux container.
http://www.material-ui.com/#/components/text-field

Categories

Resources