title: useFormik cause too many re-renders
body: <p>Stack:</p>
  <ul>
  <li>NextTS</li>
  <li>Formik</li>
  <li>Redux Toolkit</li>
  <li>MUI5</li>
  <li>Yup</li>
  </ul>
  <p>I have a page User, which displays a list of users. This page has delete user and edit user actions as well. Edit user, saves the selected user in Redux state and opens an MUI5 SwipeableDrawer with a new component UserDetail. Inside the UserDetail page, the selectedUser is fetch using useState and set to the form enhanced with Formik. But it's failing with &quot;Too many re-renders&quot; error.</p>
  <p>Here are the relevant files:</p>
  <p>Users component:</p>
  <pre><code>import React, {useState} from 'react';
  import {
    Alert,
    Box,
    Button,
    ButtonGroup,
    CircularProgress,
    Container,
    Dialog,
    DialogActions,
    DialogContent,
    DialogContentText,
    DialogTitle,
    Snackbar,
    SwipeableDrawer,
    Table,
    TableBody,
    TableCell,
    TableContainer,
    TableFooter,
    TableHead,
    TableRow
  } from &quot;@mui/material&quot;;
  import moment from &quot;moment&quot;;
  import {Delete, Edit, PersonAdd} from &quot;@mui/icons-material&quot;;
  import {useAppDispatch} from 'services/hooks';
  
  import {useRouter} from &quot;next/router&quot;;
  import {NextPage} from &quot;next&quot;;
  import {useDeleteUserMutation, useGetUsersQuery} from &quot;../../services/UserService&quot;;
  import Footer from &quot;../../components/Footer/Footer&quot;;
  import UserDetail from &quot;./components/UserDetail&quot;;
  import {UserType} from &quot;../../services/types/UserType&quot;;
  import {setUser} from &quot;../../services/slices/UserSlice&quot;;
  
  const EMPTY_DIALOG = {
    open: false,
    text: '',
    title: '',
    onConfirm: () =&gt; {
    },
    onCancel: () =&gt; {
    }
  }
  
  const EMPTY_ALERT = {
    open: false,
    text: '',
  };
  
  const Users: NextPage = () =&gt; {
  
    console.log('users')
  
    const router = useRouter();
    const dispatch = useAppDispatch();
    const [offset, setOffset] = useState(0);
    const [limit, setLimit] = useState(10);
    const [dialog, setDialog] = useState(EMPTY_DIALOG);
    const [alert, setAlert] = useState(EMPTY_ALERT);
  
    const {
      data,
      error,
      isLoading: isUsersLoading,
      isSuccess: isUsersQueried,
      isFetching: isUsersFetching,
      isError: isUsersError
    } = useGetUsersQuery();
  
    const [deleteUser, {
      data: deletedUser,
      isLoading: isUserDeleting,
      isSuccess: isUserDeleted
    }] = useDeleteUserMutation();
  
    const drawerBleeding = 56;
    const [openDrawer, setOpenDrawer] = React.useState(false);
  
    const handleDeleteUser = (userId: number) =&gt; async () =&gt; {
      try {
        await deleteUser(userId).unwrap();
        setAlert({
          open: true,
          text: `Successfully deleted user: ${userId}`,
        });
        resetDeleteDialog();
  
      } catch (error) {
        console.log(`Error: Failed deleting user with id ${userId}`);
      }
    };
  
    const resetDeleteDialog = () =&gt; {
      setDialog(EMPTY_DIALOG);
    }
  
    const openDeleteDialog = (userId: number) =&gt; () =&gt; {
      setDialog({
        open: true,
        title: 'Delete user',
        text: `Delete user: ${userId}?`,
        onConfirm: handleDeleteUser(userId),
        onCancel: () =&gt; resetDeleteDialog()
      });
    }
  
    const resetAlert = () =&gt; {
      setAlert(EMPTY_ALERT);
    }
  
    const editUser = (user: UserType) =&gt; () =&gt; {
  
      setOpenDrawer(true);
      dispatch(setUser(user));
    };
  
    const toggleEditDrawer = (newOpen: boolean) =&gt; () =&gt; {
      setOpenDrawer(newOpen);
    };
  
    const renderTable = (users: UserType[], count: number) =&gt; {
      const hasUsers = count &gt; 0;
  
      return (
          &lt;React.Fragment&gt;
            &lt;TableContainer&gt;
              &lt;Table&gt;
                &lt;TableHead&gt;
                  &lt;TableRow&gt;
                    &lt;TableCell colSpan={6} align=&quot;right&quot;&gt;
                      &lt;Button variant=&quot;outlined&quot; color=&quot;primary&quot; onClick={toggleEditDrawer(true)}&gt;
                        &lt;PersonAdd/&gt;
                      &lt;/Button&gt;
                    &lt;/TableCell&gt;
                  &lt;/TableRow&gt;
                  &lt;TableRow&gt;
                    &lt;TableCell&gt;Id&lt;/TableCell&gt;
                    &lt;TableCell&gt;First name&lt;/TableCell&gt;
                    &lt;TableCell&gt;Last name&lt;/TableCell&gt;
                    &lt;TableCell&gt;Email&lt;/TableCell&gt;
                    &lt;TableCell&gt;Birth date&lt;/TableCell&gt;
                    &lt;TableCell&gt;&lt;/TableCell&gt;
                  &lt;/TableRow&gt;
                &lt;/TableHead&gt;
                &lt;TableBody&gt;
                  {hasUsers ? (
                      users.map((user) =&gt; (
                          &lt;TableRow key={user.id}&gt;
                            &lt;TableCell&gt;{user.id}&lt;/TableCell&gt;
                            &lt;TableCell&gt;{user.firstName}&lt;/TableCell&gt;
                            &lt;TableCell&gt;{user.lastName}&lt;/TableCell&gt;
                            &lt;TableCell&gt;{user.email}&lt;/TableCell&gt;
                            &lt;TableCell&gt;
                              {moment.utc(user.birthDate).format('MM-DD-YYYY')}
                            &lt;/TableCell&gt;
                            &lt;TableCell sx={{textAlign: &quot;right&quot;}}&gt;
                              &lt;ButtonGroup&gt;
                                &lt;Button onClick={editUser(user)}&gt;
                                  &lt;Edit/&gt;
                                &lt;/Button&gt;
                                &lt;Button onClick={openDeleteDialog(user.id)}&gt;
                                  {&lt;Delete/&gt;}
                                &lt;/Button&gt;
                              &lt;/ButtonGroup&gt;
                            &lt;/TableCell&gt;
                          &lt;/TableRow&gt;
                      ))
                  ) : (
                      &lt;TableRow&gt;
                        &lt;TableCell colSpan={6}&gt;No users found.&lt;/TableCell&gt;
                      &lt;/TableRow&gt;
                  )}
                &lt;/TableBody&gt;
                &lt;TableFooter&gt;
                  &lt;TableRow&gt;
                    {/*&lt;TablePagination*/}
                    {/*    component={TableCell}*/}
                    {/*    count={count}*/}
                    {/*    page={offset}*/}
                    {/*    rowsPerPage={limit}*/}
                    {/*    onChangePage={handleChangePage}*/}
                    {/*    onChangeRowsPerPage={handleChangeRowsPerPage}*/}
                    {/*/&gt;*/}
                  &lt;/TableRow&gt;
                &lt;/TableFooter&gt;
              &lt;/Table&gt;
            &lt;/TableContainer&gt;
            &lt;SwipeableDrawer
                anchor=&quot;bottom&quot;
                open={openDrawer}
                onClose={toggleEditDrawer(false)}
                onOpen={toggleEditDrawer(true)}
                swipeAreaWidth={drawerBleeding}
                disableSwipeToOpen={false}
                ModalProps={{
                  keepMounted: true,
                }}
            &gt;
              &lt;UserDetail toggleEditDrawer={toggleEditDrawer}&gt;&lt;/UserDetail&gt;
            &lt;/SwipeableDrawer&gt;
          &lt;/React.Fragment&gt;
      );
    }
  
    const renderBody = () =&gt; {
      if (isUsersQueried) {
        const {users, count} = data;
  
        return (isUsersFetching || isUsersLoading) ?
            &lt;Box sx={{display: 'flex'}}&gt;
              &lt;CircularProgress/&gt;
            &lt;/Box&gt; :
            renderTable(users, count)
      }
    }
  
    const renderError = () =&gt; {
      return isUsersError &amp;&amp; &lt;Alert severity=&quot;error&quot;&gt;{error}&lt;/Alert&gt;;
    }
  
    return (
        &lt;Container maxWidth={&quot;md&quot;} fixed&gt;
          {renderError()}
          {renderBody()}
          &lt;Footer&gt;&lt;/Footer&gt;
          &lt;Dialog
              open={dialog.open}
              onClose={dialog.onCancel}
              aria-labelledby=&quot;alert-dialog-title&quot;
              aria-describedby=&quot;alert-dialog-description&quot;
          &gt;
            &lt;DialogTitle id=&quot;alert-dialog-title&quot;&gt;
              {dialog.title}
            &lt;/DialogTitle&gt;
            &lt;DialogContent&gt;
              &lt;DialogContentText id=&quot;alert-dialog-description&quot;&gt;
                {dialog.text}
              &lt;/DialogContentText&gt;
            &lt;/DialogContent&gt;
            &lt;DialogActions&gt;
              &lt;Button onClick={dialog.onCancel}&gt;Disagree&lt;/Button&gt;
              &lt;Button onClick={dialog.onConfirm} autoFocus&gt;
                Agree
              &lt;/Button&gt;
            &lt;/DialogActions&gt;
          &lt;/Dialog&gt;
          &lt;Snackbar
              open={alert.open}
              autoHideDuration={6000}
              onClose={resetAlert}
              message={alert.text}
          /&gt;
        &lt;/Container&gt;
    );
  }
  
  export default Users;
  </code></pre>
  <p>UserDetail component:</p>
  <pre><code>import React, {useState} from 'react';
  import {Alert, Box, Button, Container, Grid, TextField, Typography} from &quot;@mui/material&quot;;
  import {useSelector} from &quot;react-redux&quot;;
  import * as yup from 'yup';
  import {useFormik} from &quot;formik&quot;;
  import AdapterMoment from '@mui/lab/AdapterMoment';
  import LocalizationProvider from '@mui/lab/LocalizationProvider';
  import {DatePicker} from &quot;@mui/lab&quot;;
  import {NextPage} from &quot;next&quot;;
  import Footer from &quot;../../../components/Footer/Footer&quot;;
  import {useCreateUserMutation, useUpdateUserMutation} from &quot;../../../services/UserService&quot;;
  import {UserType} from &quot;../../../services/types/UserType&quot;;
  import {AppProps} from &quot;next/app&quot;;
  import {selectUser} from &quot;../../../services/slices/UserSlice&quot;;
  
  const validationSchema = yup.object({
    email: yup
    .string()
    .trim()
    .email('Please enter a valid email address')
    .required('Email is required.'),
    firstName: yup
    .string()
    .required('Please specify your first name'),
    lastName: yup
    .string()
    .required('Please specify your first name'),
    birthDate: yup
    .date()
  });
  
  const INITIAL_USER = {
    firstName: '',
    lastName: '',
    email: ''
  }
  
  const UserDetail: NextPage = ({toggleEditDrawer}: AppProps) =&gt; {
  
    console.log('user detail')
  
    const [birthDate, setBirthDate] = useState(null);
    const [pageError, setPageError] = useState(null);
    const user = useSelector(selectUser);
  
    const [createUser, {
      isLoading: isUserCreating,
      isSuccess: isUserCreated
    }] = useCreateUserMutation();
  
    // you can get the detailed user if really needed
    // const {
    //   data: user,
    //   isLoading: isUserLoading
    // } = useGetUserQuery(user.id);
  
    const [updateUser, {isLoading: isUserUpdating}] = useUpdateUserMutation();
  
    const onSubmit = (values: UserType) =&gt; {
  
      let newValues = {
        ...values,
        birthDate: birthDate.toISOString()
      }
  
      try {
        if (user &amp;&amp; user.id) {
          newValues.id = user.id;
          updateUser(newValues).unwrap();
  
        } else {
          createUser(newValues).unwrap();
        }
  
      } catch (error) {
        setPageError(error);
  
      } finally {
        toggleEditDrawer(false)();
      }
    }
  
    const formik = useFormik({
      initialValues: INITIAL_USER,
      validationSchema: validationSchema,
      onSubmit
    });
  
    const renderForm = () =&gt; {
  
      console.log(user.birthDate)
      setBirthDate(moment(user.birthDate));
      
      // this part of the code causes the too many re-render error
      formik.setValues({
        firstName: user.firstName,
        lastName: user.lastName,
        email: user.email
      });
  
      return (
          &lt;form onSubmit={formik.handleSubmit}&gt;
            &lt;Grid container spacing={4}&gt;
              &lt;Grid item xs={12}&gt;
                &lt;Typography variant={'subtitle2'} sx={{marginBottom: 2}}&gt;
                  Enter your email
                &lt;/Typography&gt;
                &lt;TextField
                    label=&quot;Email *&quot;
                    variant=&quot;outlined&quot;
                    name={'email'}
                    fullWidth
                    value={formik.values.email}
                    onChange={formik.handleChange}
                    error={formik.touched.email &amp;&amp; Boolean(formik.errors.email)}
                    helperText={formik.touched.email &amp;&amp; formik.errors.email}
                /&gt;
              &lt;/Grid&gt;
              &lt;Grid item xs={12}&gt;
                &lt;Typography variant={'subtitle2'} sx={{marginBottom: 2}}&gt;
                  Enter your firstname
                &lt;/Typography&gt;
                &lt;TextField
                    label=&quot;Firstname *&quot;
                    variant=&quot;outlined&quot;
                    name={'firstName'}
                    fullWidth
                    value={formik.values.firstName}
                    onChange={formik.handleChange}
                    error={formik.touched.firstName &amp;&amp; Boolean(formik.errors.firstName)}
                    helperText={formik.touched.firstName &amp;&amp; formik.errors.firstName}
                /&gt;
              &lt;/Grid&gt;
              &lt;Grid item xs={12}&gt;
                &lt;Typography variant={'subtitle2'} sx={{marginBottom: 2}}&gt;
                  Enter your lastName
                &lt;/Typography&gt;
                &lt;TextField
                    label=&quot;Lastname *&quot;
                    variant=&quot;outlined&quot;
                    name={'lastName'}
                    fullWidth
                    value={formik.values.lastName}
                    onChange={formik.handleChange}
                    error={formik.touched.lastName &amp;&amp; Boolean(formik.errors.lastName)}
                    helperText={formik.touched.lastName &amp;&amp; formik.errors.lastName}
                /&gt;
              &lt;/Grid&gt;
              &lt;Grid item xs={12}&gt;
                &lt;Typography variant={'subtitle2'} sx={{marginBottom: 2}}&gt;
                  Enter your birthdate
                &lt;/Typography&gt;
                &lt;LocalizationProvider dateAdapter={AdapterMoment}&gt;
                  &lt;DatePicker
                      label=&quot;Birthdate&quot;
                      value={birthDate}
                      onChange={(newValue) =&gt; {
                        setBirthDate(newValue);
                      }}
                      renderInput={(params) =&gt; &lt;TextField {...params} variant={&quot;outlined&quot;} fullWidth required/&gt;}
                  /&gt;
                &lt;/LocalizationProvider&gt;
              &lt;/Grid&gt;
              &lt;Grid item xs={12}&gt;
                &lt;Typography variant=&quot;subtitle2&quot; gutterBottom&gt;
                  Fields that are marked with * sign are required.
                &lt;/Typography&gt;
                &lt;Grid container spacing={2}&gt;
                  &lt;Grid item&gt;
                    &lt;Button
                        size=&quot;large&quot;
                        variant=&quot;contained&quot;
                        color=&quot;primary&quot;
                        type={&quot;submit&quot;}
                    &gt;
                      Save
                    &lt;/Button&gt;
                  &lt;/Grid&gt;
                  &lt;Grid item&gt;
                    &lt;Button size=&quot;large&quot; variant=&quot;contained&quot; color=&quot;secondary&quot; onClick={toggleEditDrawer(false)}&gt;
                      Cancel
                    &lt;/Button&gt;
                  &lt;/Grid&gt;
                &lt;/Grid&gt;
              &lt;/Grid&gt;
            &lt;/Grid&gt;
          &lt;/form&gt;
      );
    }
  
    return (
        &lt;Container maxWidth={&quot;md&quot;}&gt;
          &lt;Box sx={{margin: 2}}&gt;
            {pageError &amp;&amp; &lt;Alert severity=&quot;error&quot;&gt;{pageError}&lt;/Alert&gt;}
  
            &lt;Box marginBottom={4}&gt;
              &lt;Typography
                  sx={{
                    textTransform: 'uppercase',
                    fontWeight: 'medium',
                  }}
                  gutterBottom
                  color={'text.secondary'}
              &gt;
                Create User
              &lt;/Typography&gt;
              &lt;Typography color=&quot;text.secondary&quot;&gt;
                Enter the details
              &lt;/Typography&gt;
            &lt;/Box&gt;
          &lt;/Box&gt;
          {user &amp;&amp; renderForm()}
          &lt;Footer&gt;&lt;/Footer&gt;
        &lt;/Container&gt;
    );
  }
  
  export default UserDetail;
  </code></pre>
  <p>The code formik.setValues causes the too many re-render error. Any idea how to solve it? Thanks.</p>