React Labels not displaying in browser - reactjs

I am new to React and especially formik. I am trying to learn how to create a login form using formik that will display a label called email with the email input. A label called password with the password input and a button called submit.
My problem is that only the two inputs and submit button displays in the browser. The two labels for email and password do not display in the browser. Please advise how I can fix this.
App.js:
import React from 'react';
import './App.css';
import FormikContainer from './components/FormikContainer';
import LoginForm from './components/LoginForm';
function App() {
return (
<div>
<LoginForm />
</div>
);
}
export default App;
LoginForm.js:
import React from 'react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import FormikContainer from './FormikContainer';
import FormikControl from './FormikControl';
function LoginForm() {
const initialValues = {
email: '',
password: ''
};
const validationschema = Yup.object({
email: Yup.string().email('invalid email format').required('Requird'),
password: Yup.string().required('Required')
});
const onSubmit = (values) => {
console.log('Form data', values);
};
return (
<div>
<Formik
initialValues={initialValues}
validationschema={validationschema}
onSubmit={onSubmit}
>
{(formik) => {
return (
<Form>
<FormikControl
control="input"
type="email"
label="Email"
name="email"
/>
<FormikControl
control="input"
type="password"
label="Password"
name="password"
/>
<button type="submit" disabled={!formik.isValid}>
Submit
</button>
</Form>
);
}}
</Formik>
</div>
);
}
export default LoginForm;
FormikControl.js:
import React from 'react';
function FormikControl(props) {
const { control, ...rest } = props;
switch (control) {
case 'input':
return <input {...rest} />;
case 'textarea':
case 'select':
case 'radio':
case 'checkbox':
case 'date':
default:
return null;
}
return <div></div>;
}
export default FormikControl;
FormikContainer.js
import React from 'react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import FormikControl from './FormikControl';
function FormikContainer() {
const initialValues = {
email: ''
};
const validationschema = Yup.object({
email: Yup.string().required('Required')
});
const onSubmit = (values) => console.log('Form data', values);
return (
<div>
<Formik
initialValues={initialValues}
validationschema={validationschema}
onSubmit={onSubmit}
>
{(formik) => (
<Form>
<FormikControl
control="input"
type="email"
label="Email"
name="email"
/>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
);
}
export default FormikContainer;

I would like to do easily this instead :
function FormikControl({ control, id, label, ...rest }) {
return (
<>
{control === "input" && <label htmlFor={id}>{label}</label>}
<input id={id} {...rest} />
</>
);
}
export default FormikControl;

Related

React radio buttons not displaying in browser

I am new to React and formik and struggling to get my radio buttons to display in the browser. I think it is because the radio buttons are not defined as props in my formikcontrol function. How do I add it to the other props in my formikcontrol function? Please advise how to solve this issue. Thanks in advance
App.js:
import React from 'react';
import './App.css';
import FormikContainer from './components/FormikContainer';
import LoginForm from './components/LoginForm';
import Registrationform from './components/RegistrationForm';
function App() {
return (
<div>
<LoginForm />
<Registrationform />
</div>
);
}
export default App;
RegistrationForm:
import React from 'react';
import { Formik, Form } from 'formik';
import * as yup from 'yup';
import FormikControl from './FormikControl';
function Registrationform() {
const options = [
{ key: 'Email', value: 'emailmoc' },
{ key: 'Telephone', vlaue: 'telephonemoc' }
];
const initialValues = {
email: '',
password: '',
confirmPassword: '',
modeOfContact: '',
phone: ''
};
const validationSchema = yup.object({
email: yup.string().email('Invalid email format').required('Required'),
password: yup.string().required('Required'),
confirmPassword: yup
.string()
.oneOf([yup.ref('password'), ''], 'Passwords must match')
.required('required'),
modeOfContact: yup.string().required('Required'),
phone: yup.string().when('modeOfContact', {
is: 'telephonemoc',
then: yup.string().required('Required')
})
});
const onSubmit = (values) => {
console.log('Form data', values);
};
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
>
{(formik) => {
return (
<Form>
<FormikControl
control="input"
type="email"
label="Email"
name="email"
/>
<FormikControl
control="input"
type="password"
label="Password"
name="password"
/>
<FormikControl
control="input"
type="password"
label="Confirm Password"
name="confirmPassword"
/>
<FormikControl
control="radio"
type="Mode of contact"
label="modeOfContact"
options={options}
/>
<FormikControl
control="input"
type="text"
label="Phone number"
name="phone"
/>
<button type="submit" disabled={!formik.isValid}>
Submit
</button>
</Form>
);
}}
</Formik>
);
}
export default Registrationform;
FormikControl:
import React from 'react';
function FormikControl({ control, id, label, ...rest }) {
return (
<>
{control === 'input' && <label htmlFor={id}>{label}</label>}
<input id={id} {...rest} />
</>
);
}
export default FormikControl;
FormikContainer:
import React from 'react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import FormikControl from './FormikControl';
function FormikContainer() {
const initialValues = {
email: '',
password: ''
};
const validationschema = Yup.object({
email: Yup.string().required('Required')
});
const onSubmit = (values) => console.log('Form data', values);
return (
<div>
<Formik
initialValues={initialValues}
validationschema={validationschema}
onSubmit={onSubmit}
>
{(formik) => (
<Form>
<FormikControl
control="input"
type="email"
label="Email"
name="email"
/>
<FormikControl
control="input"
type="password"
label="Password"
name="password"
/>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
);
}
export default FormikContainer;
In order to get radio button rendered using Formik, you have to import Field component and use it by sending type="radio", here is how it should look like:
enter link description here
Or by using the same component you created but with type="radio" :
<FormikControl
control="input"
type="radio"
.....
/>

Formik form submitting empty object

I'm new to react and Formik and I'm trying to create a login form.
For some reason, the request to the API is sent as the default initial object I created.
Here is the code:
import { Formik, Form } from 'formik';
import { observe } from 'mobx';
import { observer } from 'mobx-react';
import React from 'react';
import { Input , Button } from 'semantic-ui-react';
import { useStore } from '../application/stores/store';
export default observer(function LoginForm() {
const {userStore} = useStore();
return (
<Formik
initialValues={{user:'', password:''}}
onSubmit={(values) => {
console.log(JSON.stringify(values, null, 2));
userStore.login(values)}
}
>
{({handleSubmit ,isSubmitting})=> (
<Form className='ui form' onSubmit={handleSubmit} autoComplete='off'>
<Input name='user' placeholder='User'/>
<Input name='password' placeholder='Password' type='password'/>
<Button loading={isSubmitting} positive content ='Login' type='submit' fluid/>
</Form>
)
}
</Formik>
)
})
and here is the result:
{
"user": "",
"password": ""
}
Use handleChange from formik
Please see this
export default observer(function LoginForm() {
const {userStore} = useStore();
return (
<Formik
initialValues={{user:'', password:''}}
onSubmit={(values) => {
console.log(JSON.stringify(values, null, 2));
userStore.login(values)}
}
>
{({handleSubmit ,isSubmitting,handleChange})=> (
<Form className='ui form' onSubmit={handleSubmit} autoComplete='off'>
<Input name='user' placeholder='User' onChange={handleChange}/>
<Input name='password' placeholder='Password' type='password' onChange={handleChange}/>
<Button loading={isSubmitting} positive content ='Login' type='submit' fluid/>
</Form>
)
}
</Formik>
)
})
I added enableReinitialize={true} and it worked for me.
Example:
<Formik
initialValues={registrationValues}
validationSchema={RegisterValidationSchema}
onSubmit={submitHandlerRegister}
enableReinitialize={true}
>
.....

Fetch and use response to change state in React

I would like to change the state of a component based on the response of a PUT request using react-refetch.
Especially when the response of the PUT is unsuccessful, as is the case with for example a 500 response.
The following example is an example in a form. When a user submits the form it should then fire off a PUT.
If the PUT response is fulfilled, it should reset the form. Otherwise nothing should happen, and the user should be able to retry.
./MyForm.jsx
import React from "react";
import PropTypes from "prop-types";
import { PromiseState } from "react-refetch";
import { Formik, Form, Field, ErrorMessage } from "formik";
import ResetOnSuccess from "./ResetOnSuccess";
const MyForm = ({ settingsPut, settingsPutResponse }) => {
const submitForm = (values, formik) => {
settingsPut(true);
// Here it should pick up the settingsPutResponse,
// and then do the following ONLY if it's successful:
//
// formik.resetForm({ values });
// window.scrollTo(0, 0);
};
return (
<div>
<Formik
noValidate
initialValues={{ name: "", password: "" }}
onSubmit={submitForm}
>
{({ dirty }) => (
<Form>
<ResetOnSuccess settingsPutResponse={settingsPutResponse} />
<Field type="text" name="name" />
<ErrorMessage name="name" component="div" />
<Field type="password" name="password" />
<ErrorMessage name="password" component="div" />
<button type="submit" disabled={dirty !== null ? !dirty : false}>
Submit
</button>
{settingsPutResponse && settingsPutResponse.rejected && (
<p style={{ color: "red" }}>Please try again</p>
)}
</Form>
)}
</Formik>
</div>
);
};
MyForm.propTypes = {
settingsPut: PropTypes.func.isRequired,
settingsPutResponse: PropTypes.instanceOf(PromiseState)
};
MyForm.defaultProps = {
userSettingsPutResponse: null
};
export default MyForm;
I might have a solution by creating a component:
./ResetOnSuccess.jsx
import React, { useEffect, useState } from "react";
import { useFormikContext } from "formik";
import PropTypes from "prop-types";
import { PromiseState } from "react-refetch";
const ResetOnSuccess = ({ settingsPutResponse }) => {
const { values, resetForm } = useFormikContext();
const [success, setSuccess] = useState(false);
useEffect(() => {
if (settingsPutResponse && settingsPutResponse.fulfilled) {
setSuccess(true);
}
}, [settingsPutResponse]);
// only if settingsPutResponse is fulfilled will it reset the form
if (success) {
resetForm({ values });
window.scrollTo(0, 0);
setSuccess(false);
}
return null;
};
ResetOnSuccess.propTypes = { settingsPutResponse: PropTypes.instanceOf(PromiseState) };
ResetOnSuccess.defaultProps = { settingsPutResponse: null };
export default ResetOnSuccess;
And then in ./MyForm.jsx add the reset component:
<Formik
noValidate
initialValues={{ name: "", password: "" }}
onSubmit={submitForm}
>
{({ dirty }) => (
<Form>
<ResetOnSuccess settingsPutResponse={settingsPutResponse} />
<Field type="text" name="name" />
<ErrorMessage name="name" component="div" />
<ResetOnSuccess settingsPutResponse={settingsPutResponse} />
// etc...
But since it's a component that returns a 'null'. This feels a bit like an anti-pattern.
Is there a better way?
I've created an codesandbox example here: https://codesandbox.io/s/quizzical-johnson-dberw

React Bootstrap + Formik - show errors after I click submit button

In my app I use React Bootstrap and Formik. I want the bootstrap to show that field is invalid (for example is not an email but should be) after I press the submit button. And then when I start typing new values to fields it should disappear. In the tutorial I used I only found the way to show that field is invalid only at the same moment the user is typing the values?
How to do that? How to set isInvalid to show errors only after submit using Formik?
Here is my current code
import * as yup from "yup";
import React from "react";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import {Formik} from "formik";
import {loginActions} from "../_actions/loginActions";
import {connect} from "react-redux";
import {loginService} from "../_services";
const schema = yup.object().shape({
username: yup.string().email("Login musi być w formie e-mail").required("Wypełnij pole login"),
password: yup.string().required("Wypełnij pole hasło")
});
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
const {username, password} = e;
if (username && password) {
loginService
.login(username, password)
.then(
success => {
const data = success.data;
if (success.status === 200 && data.success === true) {
return {...data.user, password: password};
} else if (success.status === 400) {
window.location.reload();
}
const error = (!data.success && "Wrong credentials") || success.statusText;
return Promise.reject(error);
}
)
.then(auth => {
this.props.login(auth)
})
}
}
render() {
return (
<Formik
validationSchema={schema}
onSubmit={e => this.handleSubmit(e)}
initialValues={{username: '', password: ''}}>
{
formProps => (
<Form name='form' onSubmit={formProps.handleSubmit}>
<Form.Group noValidate controlId="loginForm.username">
<Form.Label>Adres e-mail</Form.Label>
<Form.Control
type="text"
name="username"
value={formProps.values.username}
onChange={formProps.handleChange}
isInvalid={!!formProps.errors.username}
/>
<Form.Control.Feedback type="invalid">
{formProps.errors.username}
</Form.Control.Feedback>
</Form.Group>
<Form.Group controlId="loginForm.password">
<Form.Label>Hasło</Form.Label>
<Form.Control
type="password"
name="password"
value={formProps.values.password}
onChange={formProps.handleChange}
>
</Form.Control>
<Form.Control.Feedback type="invalid">
{formProps.errors.password}
</Form.Control.Feedback>
</Form.Group>
<Form.Group controlId="loginForm.loginBtn">
<Button variant="primary" type="submit">
Zaloguj się
</Button>
{formProps.isSubmitting &&
(
<img
src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA=="
/>)
}
</Form.Group>
</Form>
)
}
</Formik>
)
}
}
function mapState(state) {
const {session} = state;
return {session}
}
const connectedLoginForm = connect(mapState, {login: loginActions.login})(LoginForm);
export {connectedLoginForm as LoginForm};
Formik validation runs, onChange, onBlur and onSubmit respectively. So in your case if you want it to be validated only on submit, you should pass validateOnChange,validateOnBlur props as false.
<Formik
initialValues={{ email: '', password: '' }}
validationSchema={LoginSchema}
validateOnChange={false}
validateOnBlur={false}
onSubmit={values => onLogin(values)}>
...
/>

Handling onSubmit outside of formik

I'm trying to extract values from a form constructed with Formik, however I can't seem to get these form values out of formik via the onSubmit call. What's the best way to get his out and handle it on a parent component?
This is the form component
import React from 'react';
import { Formik, Form, Field } from 'formik';
import * as Yup from 'yup';
import moment from 'moment';
import Header from './Header';
moment.locale('en-gb');
const JoinForm = (props) => {
const initialValues = {
firstName: props.firstName || 'test'
};
return (
<div>
<Formik
initialValues={initialValues}
onSubmit={props.onSubmit}
// onSubmit={(values, actions) => {
// //console.log('this is direct from form',values)
// actions.setSubmitting(false);
// return values;
// }}
>
{props => {
const {
values,
touched,
isSubmitting,
handleChange,
handleSubmit,
handleReset,
handleBlur
} = props;
return (
<Form>
<label>First Name </label>
<Field
type="text"
name="firstName"
placeholder="First Name"
values={values.firstName}
onChange={handleChange}
onBlur={handleBlur}
/>
<button
type="button"
type="submit"
disabled={!!isSubmitting}
>
Submit
</button>
</Form>
);
}}
</Formik>
</div>
);
};
export default JoinForm;
This is the parent component
import JoinForm from './JoinForm';
import React from 'react';
import Header from './Header';
import { startAddAthlete } from '../actions/form';
class JoinPage extends React.Component{
onSubmit = (athlete) => {
console.log(athlete);
};
render() {
return (
<div>
<Header />
<JoinForm firstName={'King'} onSubmit={this.onSubmit} />
</div>
);
}
}
export default JoinPage
Am I doing this correctly? The purpose is to let the parent handle the submission and as the form is supposed to be re-usable for edits, etc
I think, your button is causing this issue. You've added both type="button" and type="submit" on the button. Removing the type="button" should fix this.
<button type="submit"
disabled={!!isSubmitting}>
Submit
</button>
Hope this will help.

Resources