I have been trying to come up with a way implement the submit function of the react-apollo <Mutation> component. Found some examples out there that seem to be an overkill for this simple task, including this and this. Since I am fresh programmer just starting to learn React, let alone Formik or even HOCs (I guess that's the way to go?), I can't really wrap my head around these examples and how to adapt them to my analogue Hello world code.
Here's my sign up form:
import React, { Component } from "react";
import { withFormik, Form, Field } from "formik";
import { Mutation } from "react-apollo";
import { gql } from "apollo-boost";
const CREATE_USER_MUTATION = gql`
mutation CREATE_USER_MUTATION(
$name: String!
$email: String!
$password: String!
) {
signup(name: $name, email: $email, password: $password) {
id
name
email
password
permissions
}
}
`;
class App extends Component {
state = {
name: "",
email: "",
password: ""
};
render() {
return (
<Mutation mutation={CREATE_USER_MUTATION}>
{(signup,{loading}) => (
<Form>
<Field type="text" name="name" placeholder="Name" />
<Field type="email" name="email" placeholder="Email" />
<Field type="password" name="password" placeholder="Password" />
<button type="submit" disabled={loading}>
Sign Up
</button>
</Form>
)}
</Mutation>
);
}
}
const SignupPage = withFormik({
mapPropsToValues() {
return {
name: "",
email: "",
password: ""
};
},
handleSubmit() { ... }
})(App);
export default SignupPage;
How can I access signup in handleSubmit?
Using <Formik /> would be a better way to go instead of using the withFormik() HOC.
Since <Formik /> is inside <Mutation/> (instead of the other way around) you are able to call your mutation in the onSubmit field.
https://jaredpalmer.com/formik/docs/api/formik
<Mutation mutation={CREATE_USER_MUTATION}>
{(signup,{loading}) => (
<Formik
initialValues={{ name: '', email: '', password: '' }}
onSubmit={ async (values, actions) => {
// You can access the signup mutation in here now
// You can access values.name, values.email, values.password
// You can access actions, e.g. actions.setSubmitting(false) once you've finished the mutation
}}
render={props => (
<Form onSubmit={props.handleSubmit}>
<Field
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.name}
name="name"
placeholder="Name"
/>
<Field
type="email"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.email}
name="email"
placeholder="Email"
/>
<Field
type="password"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.email}
name="password"
placeholder="Password"
/>
<button type="submit" disabled={loading}> Sign Up </button>
</Form>
)}
/>
)}
</Mutation>
Related
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"
.....
/>
I'm trying to find how to use async-await with useFormik hooks in onSubmit event.
I want to use axios library inside onSubmit event but with async await, but I'm not able to find the way how to use async await inside onSubmit event.
import React from 'react';
import { useFormik } from 'formik';
const SignupForm = () => {
const formik = useFormik({
initialValues: {
firstName: '',
lastName: '',
email: '',
},
onSubmit: values => {
alert(JSON.stringify(values, null, 2));
},
});
return (
<form onSubmit={formik.handleSubmit}>
<label htmlFor="firstName">First Name</label>
<input
id="firstName"
name="firstName"
type="text"
onChange={formik.handleChange}
value={formik.values.firstName}
/>
<label htmlFor="lastName">Last Name</label>
<input
id="lastName"
name="lastName"
type="text"
onChange={formik.handleChange}
value={formik.values.lastName}
/>
<label htmlFor="email">Email Address</label>
<input
id="email"
name="email"
type="email"
onChange={formik.handleChange}
value={formik.values.email}
/>
<button type="submit">Submit</button>
</form>
);
};
The onSubmit event receives a callback function, it can be a normal function or async function:
...
onSubmit: async (values) => {
// await something here...
},
...
Declare your "await" inside onSubmit function, and then call the api using axios after "await" keyword.
EXAMPLE: https://codesandbox.io/s/jovial-wescoff-uh2e3b
CODE:
import React from "react";
import ReactDOM from "react-dom";
import { Formik, Field, Form } from "formik";
import axios from "axios";
const Example = () => (
<div>
<h1>Sign Up</h1>
<Formik
initialValues={{
firstName: "",
lastName: "",
email: "",
password: ""
}}
onSubmit={async (values) => {
const user = await axios.get("https://reqres.in/api/login", {
email: values.email,
password: values.password
});
alert(JSON.stringify(user, null, 2));
}}
>
{({ isSubmitting }) => (
<Form>
<label htmlFor="firstName">First Name</label>
<Field name="firstName" placeholder="Eve" />
<label htmlFor="lastName">Last Name</label>
<Field name="lastName" placeholder="Holt" />
<label htmlFor="email">Email</label>
<Field name="email" placeholder="eve.holt#reqres.in" type="email" />
<label htmlFor="password">Password</label>
<Field name="password" placeholder="cityslicka" type="password" />
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>
)}
</Formik>
</div>
);
ReactDOM.render(<Example />, document.getElementById("root"));
Let's say we have a form that has two fields, first name, and last name.
I want to control this form using React Formik and I have simulated API response using setTimeout
The problem is that when API returns null for some properties, I get the dirty uncontrolled warning of React.
I'm using Formik's 'name' prop to mutually bind my JSON to my form inputs.
How can I solve this problem?
Here's my code:
export default function Home() {
const [initialValues, setInitialValues] = useState({
firstName: '',
lastName: '',
});
useEffect(() => {
console.log(initialValues);
}, [initialValues]);
useEffect(() => {
setTimeout(() => {
setInitialValues({ firstName: 'api', lastName: null });
}, 3000);
}, []);
const onSubmit = (values) => {
console.log(values);
};
const validationSchema = Yup.object({
firstName: Yup.string().required('Required'),
lastName: Yup.string().required('Required'),
});
return (
<div>
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
validationSchema={validationSchema}
validateOnChange={false}
validateOnBlur={false}
enableReinitialize
>
<Form>
<br />
<div>
<Field
type="text"
id="firstName"
name="firstName"
label="firstName"
placeholder="First Name"
/>
<ErrorMessage name="firstName" />
</div>
<br />
<div>
<Field
type="text"
id="lastName"
name="lastName"
placeholder="Last Name"
/>
<ErrorMessage name="lastName" />
</div>
<br />
<button type="submit">Submit</button>
</Form>
</Formik>
</div>
);
}
And here's an online sample in StackBlits
Don't trust the data coming back from your API to be complete, then. Pull each value you need into a new object with a default value of an empty string. Something like
let newInitialValues = {};
newInitialValues.firstName = apiResult.firstName ? apiResult.firstName : '';
newInitialValues.lastName = apiResult.lastName ? apiResult.lastName : '';
This ensures you'll always have a valid value.
My react.js registration form works fine, then decided to apply Formik to it and got stuck on 2 issues: I keep getting a TS error message on 'initialValues' from within the 'Formik' tag. Also, after switching 'input' to 'Field' tag, parameter function 'e' from onChange also displays an underline ts error. Same issue with style property from 'ErrorMessage' tag.
I am only trying to apply a simple Formik approach to my form. Any suggestions please?
import React, { useState, FormEvent, useRef } from 'react';
import { Link, withRouter, useHistory } from 'react-router-dom';
import api from '../services/api';
import PrimaryButton from "../components/PrimaryButton";
import * as Yup from 'yup';
import { useForm } from 'react-hook-form';
import { Formik, Field, Form, ErrorMessage } from 'formik';
const userSchema = Yup.object().shape({
name: Yup.string().required('Nome obrigatório.').max(60),
email: Yup.string().required('Email obrigatório'),
password: Yup.string().required('Senha obrigatória').min(4, 'Ops, curta demais.').max(10, 'Ops, longa demais.'),
})
function Register() {
const history = useHistory();
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [firstPassword, setFirstPassword] = useState('');
const [password, setPassword] = useState('');
const { register, handleSubmit, errors, getValues } = useForm();
// const inputPassword = useRef(null)
async function handleAddDev(e: FormEvent) {
// e.preventDefault();
let formData = {
name,
email,
password,
}
const isValid = await userSchema.isValid(formData);
console.log(isValid);
const response = await api.post('/users/new', formData)
console.log(response.data);
alert('Cadastro efetuado com sucesso.')
setName('');
setEmail('');
setFirstPassword('');
setPassword('');
history.push('/')
}
const myValues = {
name: "",
email: "",
firstPassword: "",
password: "",
}
return (
<>
<div id="register">
<strong>Cadastrar Usuário</strong>
<Formik
initialValues={myValues}
validationSchema={userSchema}
onSubmit={handleAddDev}>
<Form >
<div className="input-block">
<label htmlFor="name">Nome</label>
<Field
name="name"
id="name"
type="text"
placeholder= "Seu nome"
value={name}
onChange={e => setName(e.target.value)}
required />
<ErrorMessage
name="name"
component="div"
className="invalid-feedback"
style={{ color:"red", fontWeight:"bold"}}/>
</div>
<div className="input-block">
<label htmlFor="email">E-mail</label>
<Field
name="email"
id="email"
type="email"
placeholder= "Seu email"
value={email}
onChange={e => setEmail(e.target.value)}
/>
<ErrorMessage
name="email"
component="div"
className="invalid-feedback"
style={{ color:"red", fontWeight:"bold"}}/>
</div>
<div className="input-block">
<label htmlFor="email">Senha</label>
<Field
name="password"
id="password"
type="text"
placeholder= "Senha"
value={firstPassword}
onChange={e => setFirstPassword(e.target.value)}
ref={register}/>
<ErrorMessage
name="password"
component="div"
className="invalid-feedback"
style={{ color:"red", fontWeight:"bold"}}/>
</div>
<div className="input-block">
<label htmlFor="email">Confirmar Senha</label>
<Field
name="passwordConfirmation"
id="password"
type="text"
placeholder= "Confirme sua sehna"
ref={register({
validate: {
passwordEqual: value => (value === getValues().password) || 'Password confirmation error!',
}
})}
value={password}
onChange={e => setPassword(e.target.value)}
required />
<ErrorMessage
name="name"
component="div"
className="invalid-feedback"
style={{ color:"red", fontWeight:"bold"}}/>
{errors.passwordConfirmation && <p>{errors.passwordConfirmation.message}</p>}
</div>
<PrimaryButton type="submit">Cadastrar</PrimaryButton>
</Form>
</Formik>
<button><Link to='/'></Link> </button>
</div>
</>
)
}
export default withRouter(Register);
If your file extesion is tsx, you need to define a interface for the form values:
interface MyFormValues {
name: string,
email: string,
firstPassword: string,
password: string,
}
const myValues: MyFormValues = {
name: "",
email: "",
firstPassword: "",
password: "",
}
For the field error, the value and onChange is handled by formic so you don't need them anymore.
https://formik.org/docs/guides/typescript
const myValues = {
name: "",
email: "",
firstPassword: "",
password: "",
}
You do not have field name 'firstPassword'. Instead you have name="passwordConfirmation". initial values must match the field names.
Regarding second issue:
onChange = {(e: React.FormEvent<HTMLInputElement>)=>setFirstPassword(e.target.value)}
I'm new to formik and i am trying to populate an edit page where users can edit existing fields with formik. I keep getting React Hook "useEffect" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks error. I used the react useEffect hook to try and get the data from the backend.
function Edit({ history, match }) {
const { id } = match.params;
const isAdd = !id;
const initialValues = {
firstName: '',
lastName: '',
};
const validationSchema = Yup.object().shape({
firstName: Yup.string()
.required('First Name is required'),
lastName: Yup.string()
.required('Last Name is required'),
});
return (
<Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={()={}}>
{({ errors, touched, isSubmitting, setFieldValue }) => {
useEffect(() => {
if (!isAdd) {
getById(id).then(user => {
const fields = ['firstName', 'lastName'];
fields.forEach(field => setFieldValue(field, user[field], false));
});
}
}, []);
return (
<Form>
<div className="form-group col-5">
<label>First Name</label>
<Field name="firstName" type="text" className={'form-control' +
(errors.firstName && touched.firstName ? ' is-invalid' : '')} />
<ErrorMessage name="firstName" component="div" className="invalid-
feedback" />
</div>
<div className="form-group col-5">
<label>Last Name</label>
<Field name="lastName" type="text" className={'form-control' +
(errors.lastName && touched.lastName ? ' is-invalid' : '')} />
<ErrorMessage name="lastName" component="div" className="invalid-
feedback" />
</div>
</div>
</Form>
);
}}
</Formik
)}
export { Edit };