I'm trying to use my custom TextField in my RegisterForm with Yup but he's dosnt work.
All time I have a message "⚠ Champ obligatoire." after click on Submit I don't understand why but is good with a simple input.
RegisterPage.js
import React, { useRef } from "react";
import { useForm } from "react-hook-form";
import Button from "../../lib/Button";
import TextField from "../../lib/TextField";
import * as yup from "yup";
const SignupSchema = yup.object().shape({
firstName: yup.string().required("⚠ Champ obligatoire."),
});
export default function Home() {
const { register, handleSubmit, errors, watch } = useForm({
validationSchema: SignupSchema,
});
const onSubmit = (data) => console.log(data);
console.log(errors);
return (
<div style={styles.inputForm}>
<p>Inscription</p>
<form style={{ marginTop: "40%" }} onSubmit={handleSubmit(onSubmit)}>
<label style={styles.label} htmlFor="firstName">
Prénom
</label>
<TextField
style={styles.input}
name="firstName"
placeholder="Toto"
type="text"
ref={register}
/>
<br />
{errors.firstName && (
<p style={styles.error}>{errors.firstName.message}</p>
)}
<br />
<Button
style={{ marginTop: 10 }}
type="submit"
onClick={handleSubmit(onSubmit)}>
Termine ton incription
</Button>
</form>
</div>
);
}
My CustomTextField
CustomTextfield.js
import React from "react";
import PropTypes from "prop-types";
import TextField from "#material-ui/core/TextField";
function CustomField({ InputLabelProps = {}, ...props }) {
return (
<TextField
InputLabelProps={{ shrink: true, ...InputLabelProps }}
{...props}
/>
);
}
CustomField.propTypes = {
classes: PropTypes.object.isRequired,
};
export default CustomField;
Thanks in advance!
You need to use inputRef instead of ref on TextField. ref will be applied to the outermost element which will be the div rendered by FormControl; and this won't be any help with the yup integration. inputRef will forward the ref to the input element.
<TextField
style={styles.input}
name="firstName"
placeholder="Toto"
type="text"
inputRef={register}
/>
Related documentation: https://material-ui.com/api/text-field/#props
Related
i was trying for 3 days now to get array validation
working with Formik and Yup in React Native with a
FieldArray. The Validation was working but i can't
get it how to display the errors.
I have many tasks from the api where the user needs to write a
comment if the switch is false, for testing purposes
i disabled the switch to find out how to get the array validation
running.
The TaskScreen:
import React, { useState } from 'react';
import { StyleSheet, Button } from 'react-native';
import * as yup from 'yup';
import { Formik } from 'formik';
import { SubmitButton, Tasks } from "../components/forms";
const validationSchema = yup.object().shape({
tasks: yup.array().of(yup.object().shape({comment: yup.string().required('Required'),})),
});
function TasksScreen({ navigation: { navigate }, route}) {
const [tasks, setTasks] = useState(route.params.tasks);
const handleSubmit = async (values) => {
console.log(values);
};
return (
<>
<Formik
initialValues={{ tasks: [] }}
onSubmit={handleSubmit}
validationSchema={validationSchema}
>
<Tasks name="tasks" tasks={tasks} />
<SubmitButton title="Send" />
</Formik>
<Button onPress={() => navigate('TaskLists') } title="Back"/>
</>
)
}
const styles = StyleSheet.create({
container: {},
});
export default TasksScreen;
The Tasks Component
import React from 'react';
import { useFormikContext, FieldArray } from "formik";
import ErrorMessage from "./ErrorMessage";
import { TextInput, Text, View, StyleSheet, Switch } from 'react-native';
function Tasks({ tasks, name }) {
const {
setFieldTouched,
setFieldValue,
errors,
touched,
values,
} = useFormikContext();
return (
<FieldArray name={name}>
<>
{ tasks.map((task, key)=>{
return (
<>
<View key={key}>
<Text>{task.name} {`${name}[${key}].comment]`}</Text>
<TextInput
onBlur={() => setFieldTouched(`${name}[${key}].comment`)}
onChangeText={(text) => setFieldValue(`${name}[${key}].comment`, text)}
value={values[`${name}[${key}].comment`]}
name={`${name}[${key}].comment`}
placeholder="Task comment please"
/>
<ErrorMessage error={`${errors}${name}[${key}].comment`} visible={`${touched}${name}[${key}].comment`} />
</View>
</>
)
})}
</>
</FieldArray>
);
}
const styles = StyleSheet.create({
container: {},
});
export default Tasks;
I also have problems to console.log the Formik Bag, can't figure it out,
it's my first steps with RN, sorry if the question is boring.
Thanks in advance for any kind of help.
As mentioned here in the validation docs you should create a callback as a child of the Formik component which has an object combined with two params which are errors and touched:
the errors are the validation errors of the forms and the touched is map of field names to **whether** the field has been touched.
See this Sample provide by the Formik Docs.
import React from 'react';
import { Formik, Form, Field } from 'formik';
import * as Yup from 'yup';
const SignupSchema = Yup.object().shape({
firstName: Yup.string()
.min(2, 'Too Short!')
.max(50, 'Too Long!')
.required('Required'),
lastName: Yup.string()
.min(2, 'Too Short!')
.max(50, 'Too Long!')
.required('Required'),
email: Yup.string().email('Invalid email').required('Required'),
});
export const ValidationSchemaExample = () => (
<div>
<h1>Signup</h1>
<Formik
initialValues={{
firstName: '',
lastName: '',
email: '',
}}
validationSchema={SignupSchema}
onSubmit={values => {
// same shape as initial values
console.log(values);
}}
>
{({ errors, touched }) => (
<Form>
<Field name="firstName" />
{errors.firstName && touched.firstName ? (
<div>{errors.firstName}</div>
) : null}
<Field name="lastName" />
{errors.lastName && touched.lastName ? (
<div>{errors.lastName}</div>
) : null}
<Field name="email" type="email" />
{errors.email && touched.email ? <div>{errors.email}</div> : null}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
);
I'm trying to implement a form with validation:
import React from 'react';
import { useForm } from "react-hook-form";
import axios, {AxiosResponse} from "axios";
import {Box, Button, Container, Grid, Typography} from "#material-ui/core";
import TextField from '#material-ui/core/TextField';
export async function postTicket(data: TicketDTO): Promise<AxiosResponse<TicketDTO[]>> {
return await axios.post<TicketDTO[]>(
`http://localhost:8080/api/support/tickets/create`
);
}
export interface TicketDTO {
title?: string;
}
export default function OpenTicket(props: any) {
const {
register,
handleSubmit,
formState: { errors }
} = useForm<TicketDTO>();
const onSubmit = async (data: TicketDTO) => {
try {
await postTicket(data);
} catch (err) {
console.log(err);
}
};
return (
<Container>
<form onSubmit={handleSubmit(onSubmit)}>
<TextField
id="outlined-full-width"
label="Name"
{...register("title", { required: true, maxLength: 20 })}
placeholder="Placeholder"
helperText="Full width!"
fullWidth
margin="normal"
InputLabelProps={{
shrink: true,
}}
variant="outlined"
/>
<Button
variant="contained"
color="primary"
size="small"
>
Submit
</Button>
</form>
</Container>
);
}
Sandbox: https://stackblitz.com/edit/react-ts-4pnf8b?file=Hello.tsx
But when I click submit button nothing happens. I would like to use the Materia-ui default validation messages? Do you know where I'm wrong?
I have made some modifications to your code,
Added type to your button as submit
required can be directly added to Text-Field.
maxLength can be applied as part of inputProps.
I am not quite sure what you are doing in your submit event. Ignoring for now.
https://stackblitz.com/edit/react-ts-dufrzd?file=Hello.tsx
<form onSubmit={handleSubmit(onSubmit)}>
<TextField
id="outlined-full-width"
label="Name"
required
placeholder="Placeholder"
helperText="Full width!"
fullWidth
margin="normal"
InputLabelProps={{
shrink: true,
}}
inputProps={{maxLength:20}}
variant="outlined"
/>
<Button variant="contained" color="primary" size="small" type="submit">
Submit
</Button>
</form>
I'm using react-hook-form to handle form values, Its working fine for all other input types like TextFeild, Select from material but facing issues with "material-ui-chip-input" as adding tag working fine but not able to delete tag on click of cross button or hitting backspace. I'm struggling on this from a long. Anyone please help in it.
import React from "react";
import FormControl from "#material-ui/core/FormControl";
import { Controller } from "react-hook-form";
import ChipInput from "material-ui-chip-input";
const ReactHookFormChips = ({
name,
label,
control,
defaultValue,
children,
rules,
error,
chipsError,
...props
}) => {
const labelId = `${name}-label`;
return (
<FormControl {...props}>
<Controller
as={
<ChipInput
label={label}
helperText={chipsError}
error={error}
/>
}
name={name}
control={control}
defaultValue={defaultValue}
rules={rules}
/>
</FormControl>
);
};
export default ReactHookFormChips;
calling this component like
<ReactHookFormChips
id="levelLabel"
name="tags"
label="Select Tags"
control={control}
defaultValue={[]}
margin="normal"
error={!!errors?.tags}
rules={{ required: true }}
chipsError={errors?.tags && "Tag is required."}
/>
I fixed it using render prop.
import React from "react";
import FormControl from "#material-ui/core/FormControl";
import InputLabel from "#material-ui/core/InputLabel";
import { Controller } from "react-hook-form";
import ChipInput from "material-ui-chip-input";
const ReactHookFormChips = ({
name,
label,
control,
defaultValue,
children,
rules,
error,
chipsError,
...props
}) => {
const labelId = `${name}-label`;
return (
<FormControl {...props}>
<Controller
render={({ onChange, onBlur, value }) =>
<ChipInput
onChange={onChange}
label={label}
helperText={chipsError}
error={error}
/>
}
name={name}
control={control}
defaultValue={defaultValue}
rules={rules}
/>
</FormControl>
);
};
export default ReactHookFormChips;
I have a redux-form component that I correctly passing in the initial form state values but when I click inside the form I cannot edit the values. I am have followed all the documentation like the following link:
Initialize From State
And I also followed the documentation to implement a custom input:
Will redux-form work with a my custom input component?
So I am trying to figure out what am I missing? Here is my form component:
EditJournalForm.jsx
import "./styles/list-item.scss";
import {Button, ButtonToolbar} from "react-bootstrap";
import {connect} from "react-redux";
import {Field, reduxForm} from "redux-form";
import InputField from "../form-fields/InputField";
import PropTypes from "prop-types";
import React from "react";
class EditJournalForm extends React.Component {
render() {
//console.log('EditJournalForm this.props', this.props);
const {closeOverlay, handleSubmit, initialValues, pristine, submitting,} = this.props;
return (
<form onSubmit={handleSubmit}>
<div>
<div className="form-field">
<Field
component={props =>
<InputField
content={{val: initialValues.title}}
updateFn={param => props.onChange(param.val)}
{...props}
/>
}
label="Journal title"
name="title"
type="text"
/>
</div>
<div className="form-field">
<Field
component={props =>
<InputField
content={{val: initialValues.description}}
updateFn={param => props.onChange(param.val)}
{...props}
/>
}
componentClass="textarea"
label="Description"
name="description"
rows="5"
type="text"
/>
</div>
<div className="form-button-group">
<ButtonToolbar>
<Button
bsSize="small"
style={{"width": "48%"}}
onClick={() => {
if (closeOverlay) {
closeOverlay();
}
}}
>
Cancel
</Button>
<Button
bsSize="small"
disabled={pristine || submitting}
style={
{
"backgroundColor": "#999",
"width": "48%"
}}
type="submit"
>
Add
</Button>
</ButtonToolbar>
</div>
</div>
</form>
);
}
}
EditJournalForm.propTypes = {
"closeOverlay": PropTypes.func,
"handleSubmit": PropTypes.func.isRequired,
"pristine": PropTypes.bool.isRequired,
"submitting": PropTypes.bool.isRequired,
"initialValues": PropTypes.object
};
EditJournalForm.defaultProps = {
"closeOverlay": undefined
};
export default reduxForm({
form: "editJournal",
enableReinitialize: true
})(connect((state, ownProps) => {
return {
initialValues: {
"title": state.bees.entities.journals[ownProps.journal.id].attributes.title,
"description": state.bees.entities.journals[ownProps.journal.id].attributes.description,
}
};
}, undefined)(EditJournalForm));
and here is my custom input:
InputField.jsx
import {ControlLabel, FormControl, FormGroup} from "react-bootstrap";
import PropTypes from "prop-types";
import React from "react";
const InputField = ({input, label, content, updateFn, type, ...props}) => (
<FormGroup >
<ControlLabel>
{label}
</ControlLabel>
<FormControl
{...props}
{...input}
value={content}
onChange={updateFn}
type={type}
/>
</FormGroup>
);
export default InputField;
InputField.propTypes = {
"input": PropTypes.object.isRequired,
"label": PropTypes.string.isRequired,
"type": PropTypes.string.isRequired,
"content": PropTypes.object,
"updateFn": PropTypes.func
};
Try to call onChange function of input field:
const InputField = ({input, label, content, updateFn, type, ...props}) => (
<FormGroup>
<ControlLabel>
{label}
</ControlLabel>
<FormControl
{...props}
{...input}
value={content}
onChange={(e) => {
input.onChange(e);
updateFn(e);
}}
type={type}
/>
</FormGroup>
);
I see at least one problem - you are assigning the content prop as an object with a val property, but in your custom InputField, you are setting value={content}, so that is actually the object with { val: 'the value' } as opposed to the actual value ('the value' in this example).
With redux-form, it isn't necessary to manually assign from initialValues. By having a name property on the Field, it will be correctly assigned for you.
I am new to redux-form and I'm having a strange issue with handling onSubmit.
When I set up my project exactly as in the redux-form example here http://redux-form.com/6.7.0/examples/syncValidation/ it works as expected. I have attempted to extend this example for my needs and have confirmed that it works as expected when it loads the form like so: route component > form.
The issue arises when I attempt to load the form within a react component which is loaded via a route (route component > container component > form). When I click submit the field values are added to the address bar and form validation doesn't run. I have tried absolutely everything I can think of to fix this. The code provided below will work correctly if you replace <Main /> with <RegisterForm handleSubmit={showResults} /> in index.js. Any ideas where I'm going wrong here?
RegisterForm.js:
import React from 'react';
import { Field, reduxForm } from 'redux-form';
const validate = values => {
const errors = {};
if (!values.name) {
errors.name = 'Required';
} else if (values.name.length <= 2) {
errors.username = 'Must be 2 characters or more';
} else if (values.name.length > 50) {
errors.username = 'Must be 50 characters or less';
}
if (!values.email) {
errors.email = 'Required';
} else if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z] {2,4}$/i.test(values.email)) {
errors.email = 'Invalid email address';
}
if (!values.password) {
errors.password = 'Required';
} else if (!values.confirm) {
errors.confirm = 'Required';
} else if (values.password !== values.confirm) {
errors.confirm = 'Passwords do not match';
}
return errors;
};
const renderField = ({ input, label, type, id, meta: { touched, error, warning } }) => (
<div>
<label htmlFor={id}>{label}</label>
<div>
<input {...input} id={id} placeholder={label} type={type} />
{touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))}
</div>
</div>
);
const RegisterForm = (props) => {
const { handleSubmit, pristine, reset, submitting } = props
return (
<form onSubmit={handleSubmit}>
<div className="row">
<div className="medium-6 columns medium-centered">
<Field type="text" id="name" name="name" component={renderField} placeholder="name" label="Name" />
</div>
<div className="medium-6 columns medium-centered">
<Field type="text" id="email" name="email" component={renderField} placeholder="email" label="Email" />
</div>
<div className="medium-6 columns medium-centered">
<Field type="password" id="password" name="password" component={renderField} placeholder="password" label="Password" />
</div>
<div className="medium-6 columns medium-centered">
<Field type="password" id="confirm" name="confirm" component={renderField} placeholder="confirm" label="Confirm password" />
</div>
<div className="medium-6 columns medium-centered">
<button type="submit" disabled={submitting}>Submit</button>
</div>
</div>
</form>
);
};
export default reduxForm({
form: 'register', // a unique identifier for this form
validate,
})(RegisterForm);
Index.js(works):
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { HashRouter as Router, hashHistory } from 'react-router-dom';
const store = require('./store').configure();
import RegisterForm from './RegisterForm.jsx';
import Main from './Main.jsx';
const rootEl = document.getElementById('app');
const showResults = (values) => {
window.alert(`You submitted:\n\n${JSON.stringify(values, null, 2)}`);
}
ReactDOM.render(
<Provider store={store}>
<Router history={hashHistory}>
<div style={{ padding: 15 }}>
<h2>Synchronous Validation</h2>
<RegisterForm handleSubmit={showResults} />
</div>
</Router>
</Provider>,
rootEl,
);
Index.js(doesn't work):
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { HashRouter as Router, hashHistory } from 'react-router-dom';
const store = require('./store').configure();
import RegisterForm from './RegisterForm.jsx';
import Main from './Main.jsx';
const rootEl = document.getElementById('app');
const showResults = (values) => {
window.alert(`You submitted:\n\n${JSON.stringify(values, null, 2)}`);
}
ReactDOM.render(
<Provider store={store}>
<Router history={hashHistory}>
<div style={{ padding: 15 }}>
<h2>Synchronous Validation</h2>
<Main />
</div>
</Router>
</Provider>,
rootEl,
);
Main.js:
import React, { Component } from 'react';
import RegisterForm from './RegisterForm.jsx';
const showResults = (values) => {
window.alert(`You submitted:\n\n${JSON.stringify(values, null, 2)}`);
};
class Register extends Component {
render() {
return (
<RegisterForm handleSubmit={showResults} />
);
}
}
export default Register;
You should pass in your submit handler to the onSubmit prop, not handleSubmit. It comes in to your form component as handleSubmit, so that code should be fine.
class Register extends Component {
render() {
return (
//change this
<RegisterForm onSubmit={showResults} />
);
}
}