Formik: How to redirect the page using withRouter HOC after handleSubmit is successful?

Created on 11 Dec 2017  路  13Comments  路  Source: formium/formik

Can I have an example/snippet which is using react-router-v4 to redirect after successful submission? I am not sure how to use withRouter HOC with withFormik HOC?

If I could get props.history inside handleSubmit, then i can redirect the user to new page using props.history.push('/any-new-page')

const EnhancedForm = withFormik({
  mapPropsToValues: () => ({ 
    surveyTitle: '',
    surveyCategory: ''
 }),
  validationSchema: Yup.object().shape({
    surveyTitle: Yup.string().required('Survey name is required'),
    surveyCategory: Yup.string().required('Survey category is required')
  }),
  handleSubmit: (values, { setSubmitting }, history) => {
    setTimeout(() => {
      alert(JSON.stringify(values, null, 2));
      setSubmitting(false);
      // How to get history props from withRouter HOC to here
      history.push('/any-new-page');
    }, 1000);
  },
})(MyInnerForm);

Most helpful comment

I struggled with this for days and I'd like to share my solution. I'm using Formik with render props, so this is what I do instead, for a contrived example of a form with one field:

class InnerForm extends React.Component {
    handleSubmit = (values, formikBag) => {
        const { history } = this.props;

        axiosClient.post('/endpoint', {data})
            .then(function (response) {
                history.push("/redirect_url");
            })
            .catch(function (error) {
                console.log(error);
            });
    }

    render() {
        return (
            <div>
                <h1>Your form!</h1>
                <Formik
                    initialValues={{
                        username: ''
                    }}
                    onSubmit={this.handleSubmit}
                    render={({
                        values,
                        errors,
                        touched,
                        handleChange,
                        handleBlur,
                        handleSubmit,
                        isSubmitting
                    }) => (
                        <form onSubmit={handleSubmit}>
                            <div>
                                <label>Your Username</label>
                                <input name="username" type="text"
                                    className={`form-control ${errors.username && touched.username && 'is-invalid'}`}
                                    value={values.username}
                                    onChange={handleChange}
                                    onBlur={handleBlur} />
                                {errors.username && touched.username && <div className="invalid-feedback">{errors.username}</div>}
                            </div>
                            <button type="submit" disabled={isSubmitting}>
                                Submit
                            </button>
                        </form>
                    )}
                />
            </div>
        )
    }
}

const MyForm = withRouter(InnerForm);

export default MyForm;

It's also not true what @prichodko says above about handleSubmit's second argument including props, or at least it's not true anymore, as you can see here. getFormikActions() returns a list which does not include props.

All 13 comments

Second argument in handleSubmit includes props. You should be able to get history from there.

May I know how to connect withRouter HOC with MyInnerForm component which is already connected with withFormik HOC?

There are two ways how to do that.

Manually:

const InnerForm = () => {...}

const Form = withRouter(withFormik(InnerForm))

Or you can use compose utility provided from various libraries (redux, recompose, ramda...) and use it:

const Form = compose(
   withRouter,
   withFormik({ /** ... your formik config */ }),
)(InnerForm)

which is useful when you have multiple HOCs.

One important note - it is important in which order they are composed, since you need Router props to be available in the formik.


Edit by jared: withFormik --> withFormik({ /** ... */ })

I struggled with this for days and I'd like to share my solution. I'm using Formik with render props, so this is what I do instead, for a contrived example of a form with one field:

class InnerForm extends React.Component {
    handleSubmit = (values, formikBag) => {
        const { history } = this.props;

        axiosClient.post('/endpoint', {data})
            .then(function (response) {
                history.push("/redirect_url");
            })
            .catch(function (error) {
                console.log(error);
            });
    }

    render() {
        return (
            <div>
                <h1>Your form!</h1>
                <Formik
                    initialValues={{
                        username: ''
                    }}
                    onSubmit={this.handleSubmit}
                    render={({
                        values,
                        errors,
                        touched,
                        handleChange,
                        handleBlur,
                        handleSubmit,
                        isSubmitting
                    }) => (
                        <form onSubmit={handleSubmit}>
                            <div>
                                <label>Your Username</label>
                                <input name="username" type="text"
                                    className={`form-control ${errors.username && touched.username && 'is-invalid'}`}
                                    value={values.username}
                                    onChange={handleChange}
                                    onBlur={handleBlur} />
                                {errors.username && touched.username && <div className="invalid-feedback">{errors.username}</div>}
                            </div>
                            <button type="submit" disabled={isSubmitting}>
                                Submit
                            </button>
                        </form>
                    )}
                />
            </div>
        )
    }
}

const MyForm = withRouter(InnerForm);

export default MyForm;

It's also not true what @prichodko says above about handleSubmit's second argument including props, or at least it's not true anymore, as you can see here. getFormikActions() returns a list which does not include props.

It depends on if you use the hoc or render prop. The hoc includes props

This is what worked for me:

<WrappedFormComp {...props} />

Now that you passed history via prop drilling, you can write:

const formEnhancer = withFormik({
   [...]
    handleSubmit(values, { props }) {
      props.history.push('/some/funky/path')
    }
  })

I spent some time dealing with this as well. Thanks, @syntactical for your suggestion. I am using render props as well and reading the docs made it a bit more confusing. I was expecting to get props with formikBag.

@jaredpalmer I think would be good to add that differentiation between hoc and render prop in the docs

@jay-jlm man, what if user gets an error message as a response from server ? in your case he will be redirected anyway even if he entered wrong credentials

a basic working version:

import React, { Component } from 'react'
import { Formik, Field, Form } from 'formik'
import './index.css'

class LoginForm extends Component {
  handleSubmit = (values, actions) => {
    actions.setSubmitting(true)
    console.log(values)
    console.log(actions)
    const { history } = this.props
    history.push('/dashboard')
  }
  render() {
    return (
      <div>
        <h1>Admin</h1>
        <Formik
          initialValues={{
            user: '',
            passwd: '',
          }}

          validate={values => {
            let errors = {}
          }}

          onSubmit={this.handleSubmit}

          render={x => (
            <div className='login-form'>
              <Form>
                <div className='login-item'>
                  <Field name='user' type='text' placeholder='Username' />
                </div>
                <div className='login-item'>
                  <Field name='passwd' type='password' placeholder='Password' />
                </div>
                <div className='login-item'>
                  <button type='submit' disabled={x.isSubmitting}>Login</button>
                </div>
              </Form>
            </div>
          )}
        />
      </div>
    )
  }
}

export default LoginForm

Thanks @converge for your code. I don't know why... for some reason if I just access this.props.history... inside the handle method does not work. I had to extract with const { history } = this.props; to get work.

I want to share here my solution to deal with handleSubmit functions which are outside of your component context.
In this case, you cannot access directly the route variable from your props'component inside the handleSubmit function.

But you can achieve this by using these two methods :

  • On your Formik form change the onSubmit attribute like this:
    onSubmit={(formData, formMethods) => handleSubmit(props.route, formData, formMethods)}

  • Or inside your component render method use a bind call:
    handleSubmit = handleSubmit.bind(this, route);

In both case, the prototype of your handleSubmit method become:
function handleSubmit(route, formData, formMethods)

None of those solutions worked for me. So I dropped withFormik and now I'm using useFormik method.

import { useFormik } from 'formik';

export default function Form(props) {
  const formik = useFormik({
    initialValues: { email: '' },
    validationSchema: {}, // you can use Yup
    onSubmit: async (values) => {
      props.navigation.navigate('my-route')
    },
  });
  return (
    <View style={styles.container}>
      <TextInput
        label="Email"
        value={formik.values.email}
      />

      <Button onPress={formik.handleSubmit}>
        Confirm
      </Button>
    </View>
  );
}

I'm trying to redirect my form page in formik but unable to do so can some one show me how to redirect after form login

import React from "react";
import { Formik } from "formik";
import * as EmailValidator from "email-validator";
import * as Yup from "yup";
import AdminAccess from './AdminAccess'
import { Redirect, Route } from 'react-router-dom'

import data from './data'

const admindata = data.admindata;
const AdminLoginForm = (props) => (
initialValues={{ email: "", password: "" }}
onSubmit={(values, { setSubmitting }) => {
if(admindata.email == values.email & admindata.password == values.password) {
return


}

  setTimeout(() => {        
    console.log("Logging in", values);
    console.log("data", admindata.email);              
    setSubmitting(false);
  }, 500);      
}}

validationSchema={Yup.object().shape({
  email: Yup.string()
    .email()
    .required("Required"),
  password: Yup.string()
    .required("No password provided.")
    .min(8, "Password is too short - should be 8 chars minimum.")
    .matches(/(?=.*[0-9])/, "Password must contain a number.")
})}

>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
handleBlur,
handleSubmit
} = props;

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="email">Email</label>
      <input
        name="email"
        type="text"
        placeholder="Enter your email"
        value={values.email}
        onChange={handleChange}
        onBlur={handleBlur}
        className={errors.email && touched.email && "error"}
      />
      {errors.email && touched.email && (
        <div className="input-feedback">{errors.email}</div>
      )}
      <label htmlFor="email">Password</label>
      <input
        name="password"
        type="password"
        placeholder="Enter your password"
        value={values.password}
        onChange={handleChange}
        onBlur={handleBlur}
        className={errors.password && touched.password && "error"}
      />
      {errors.password && touched.password && (
        <div className="input-feedback">{errors.password}</div>
      )}
      <button type="submit" disabled={isSubmitting}>
        Login
      </button>
    </form>        

  );
}}


);

export default AdminLoginForm;

Was this page helpful?
0 / 5 - 0 ratings

Related issues

najisawas picture najisawas  路  3Comments

PeerHartmann picture PeerHartmann  路  3Comments

Jucesr picture Jucesr  路  3Comments

giulioambrogi picture giulioambrogi  路  3Comments

ancashoria picture ancashoria  路  3Comments