title: Formik onSubmit error: Can't perform a React state update on an unmounted component
body: <p>I get this error when trying to submit a Formik form.</p>
  
  <blockquote>
    <p>Warning: Can't perform a React state update on an unmounted component.
    This is a no-op, but it indicates a memory leak in your application.
    To fix, cancel all subscriptions and asynchronous tasks in a useEffect
    cleanup function.
        in Formik (at layout.tsx:113)
        in div (created by ForwardRef(CardContent))
        in ForwardRef(CardContent) (created by WithStyles(ForwardRef(CardContent)))
        in WithStyles(ForwardRef(CardContent)) (created by Context.Consumer)
        in StyledComponent (created by Styled(WithStyles(ForwardRef(CardContent))))
        in Styled(WithStyles(ForwardRef(CardContent))) (created by CardContent)
        in CardContent (created by Context.Consumer)
        in StyledComponent (created by Styled(CardContent))
        in Styled(CardContent) (at layout.tsx:112)
        in div (created by ForwardRef(Paper))
        in ForwardRef(Paper) (created by WithStyles(ForwardRef(Paper)))
        in WithStyles(ForwardRef(Paper)) (created by ForwardRef(Card))
        in ForwardRef(Card) (created by WithStyles(ForwardRef(Card)))
        in WithStyles(ForwardRef(Card)) (created by Context.Consumer)
        in StyledComponent (created by Styled(WithStyles(ForwardRef(Card))))
        in Styled(WithStyles(ForwardRef(Card))) (created by Card)
        in Card (at layout.tsx:111)</p>
  </blockquote>
  
  <p>The function <code>onSubmit</code> has no <code>async</code> calls:</p>
  
  <pre><code>&lt;Formik
    ...
    onSubmit={(values, { setSubmitting }): void =&gt; {
      setTimeout(() =&gt; {
        // convert daysOfWeek to Weekday[]
        const daysOfWeek = values.daysOfWeek
          .filter(day =&gt; day.checked)
          .map(day =&gt; day.dayOfWeek)
        const edited: EditTask = { ...task, ...values, daysOfWeek }
        onEditTask(edited)
        setSubmitting(false)
      })
    }}
  &gt;
  </code></pre>
  
  <p>The <code>onEditTask</code> callback just updates the list of <code>TaskItems</code>. </p>
  
  <p>The full <code>TaskItem</code> component looks like this. The <code>'@web/core...'</code> components are my own wrappers around Material UI components.</p>
  
  <p><strong>TaskItem.tsx</strong></p>
  
  <pre><code>import React, { ReactElement } from 'react'
  import { useTranslation } from 'react-i18next'
  import Grid from '@material-ui/core/Grid'
  import ListItemIcon from '@material-ui/core/ListItemIcon'
  import ListItemText from '@material-ui/core/ListItemText'
  import CloseIcon from '@material-ui/icons/Close'
  import Typography from '@web/core/components/Typography'
  import MenuButton from '@web/core/components/MenuButton'
  import MenuItem from '@web/core/components/MenuItem'
  import TextField from '@web/core/components/TextField'
  import Checkbox from '@web/core/components/Checkbox'
  import Button from '@web/core/components/Button'
  import Card from '@web/core/components/Card'
  import EditIcon from '@web/core/theme/icons/Edit'
  import { TaskSchedule, Weekday } from 'stores/goals/models'
  import {
    EditTask,
    getDistinctTaskSchedulesSorted,
  } from 'components/CitizenDetails/DietRules/FormDrawer'
  import { TaskItemViewMode } from 'components/CitizenDetails/DietRules/TaskItem'
  import { Formik, FieldArray, Form } from 'formik'
  import uuid from 'uuid/v4'
  import * as Yup from 'yup'
  import TaskOverview from '../TaskOverview'
  import { DeleteIcon, DeleteMenuItemText, EditCardContent, RemoveScheduleButton } from './styled'
  
  const taskValidationSchema = Yup.object().shape({
    subject: Yup.string().required('Required'),
    description: Yup.string().nullable(),
    daysOfWeek: Yup.array()
      .min(1, 'Min 1 day')
      .required('Required'),
    taskSchedules: Yup.array()
      .ensure()
      .min(0, 'Min 0 elements'),
  })
  
  interface Props {
    viewMode: TaskItemViewMode
    task: EditTask
    onChangeViewMode: (viewMode: TaskItemViewMode) =&gt; void
    onDeleteTask: () =&gt; void
    onEditTask: (task: EditTask) =&gt; void
    disableMenu?: boolean
  }
  
  export interface CheckedWeekday {
    checked: boolean
    dayOfWeek: Weekday
  }
  
  function mapToCheckedWeekday(weekday: Weekday, checked: boolean): CheckedWeekday {
    return {
      dayOfWeek: weekday,
      checked,
    }
  }
  
  export default function TaskItem({
    viewMode,
    task,
    onChangeViewMode,
    onDeleteTask,
    onEditTask,
    disableMenu,
  }: Props): ReactElement {
    const [t] = useTranslation()
  
    type TaskFormValues = Pick&lt;EditTask, 'subject' | 'description'&gt; &amp; {
      daysOfWeek: CheckedWeekday[]
      taskSchedules: TaskSchedule[]
    }
  
    const initialValues: TaskFormValues = {
      ...task,
      daysOfWeek: Object.values(Weekday).map(day =&gt;
        mapToCheckedWeekday(day, task.daysOfWeek.includes(day))
      ),
      taskSchedules: getDistinctTaskSchedulesSorted(task.taskSchedules),
    }
  
    return (
      &lt;&gt;
        {viewMode === TaskItemViewMode.View ? (
          &lt;Grid container justify="space-between" alignItems="center"&gt;
            &lt;Grid item&gt;
              &lt;TaskOverview task={task} /&gt;
            &lt;/Grid&gt;
            &lt;Grid item&gt;
              {!disableMenu &amp;&amp; (
                &lt;MenuButton id="diet-task-overview-menu-button"&gt;
                  &lt;MenuItem onClick={(): void =&gt; onChangeViewMode(TaskItemViewMode.Edit)}&gt;
                    &lt;ListItemIcon&gt;
                      &lt;EditIcon /&gt;
                    &lt;/ListItemIcon&gt;
                    &lt;ListItemText primary={t('edit')} /&gt;
                  &lt;/MenuItem&gt;
                  &lt;MenuItem key="diet-task-overview-menu-remove-button" onClick={onDeleteTask}&gt;
                    &lt;ListItemIcon&gt;
                      &lt;DeleteIcon /&gt;
                    &lt;/ListItemIcon&gt;
                    &lt;DeleteMenuItemText primary={t('remove')} /&gt;
                  &lt;/MenuItem&gt;
                &lt;/MenuButton&gt;
              )}
            &lt;/Grid&gt;
          &lt;/Grid&gt;
        ) : (
          &lt;Card&gt;
            &lt;EditCardContent&gt;
              &lt;Formik
                initialValues={initialValues}
                validationSchema={taskValidationSchema}
                onSubmit={(values, { setSubmitting }): void =&gt; {
                  setTimeout(() =&gt; {
                    // convert daysOfWeek to Weekday[]
                    const daysOfWeek = values.daysOfWeek
                      .filter(day =&gt; day.checked)
                      .map(day =&gt; day.dayOfWeek)
                    const edited: EditTask = { ...task, ...values, daysOfWeek }
                    onEditTask(edited)
                    setSubmitting(false)
                  })
                }}
              &gt;
                {({
                  values,
                  errors,
                  isValid,
                  touched,
                  dirty,
                  handleChange,
                  handleBlur,
                  handleSubmit,
                  handleReset,
                  isSubmitting,
                  validateForm,
                }): ReactElement =&gt; (
                  &lt;Form&gt;
                    &lt;Grid container direction="column" spacing={3}&gt;
                      &lt;Grid item&gt;
                        &lt;Typography variant="h6"&gt;{t('goals:diet.task.title')}&lt;/Typography&gt;
                      &lt;/Grid&gt;
                      &lt;Grid item&gt;
                        &lt;TextField
                          id={`task-${task.uuid}-subject`}
                          label={t('title')}
                          name="subject"
                          onChange={handleChange}
                          onBlur={handleBlur}
                          value={values.subject}
                          fullWidth
                          inputProps={{ autoFocus: viewMode === TaskItemViewMode.Create }}
                        /&gt;
                        {errors.subject &amp;&amp; touched.subject &amp;&amp; errors.subject}
                      &lt;/Grid&gt;
                      &lt;Grid item&gt;
                        &lt;TextField
                          id={`task-${task.uuid}-description`}
                          label={t('description')}
                          name="description"
                          onChange={handleChange}
                          onBlur={handleBlur}
                          value={values.description}
                          rows="3"
                          multiline
                          fullWidth
                        /&gt;
                      &lt;/Grid&gt;
                      &lt;Grid item&gt;
                        &lt;Typography variant="subtitle1" gutterBottom&gt;
                          {t('day').toUpperCase()}
                        &lt;/Typography&gt;
                        &lt;FieldArray
                          name="daysOfWeek"
                          render={({ replace }) =&gt; (
                            &lt;Grid container spacing={2}&gt;
                              {values.daysOfWeek.map((day, idx) =&gt; (
                                &lt;Grid item key={`task-weekday-${day.dayOfWeek}`}&gt;
                                  &lt;Checkbox
                                    id={`task-weekday-checkbox-${day.dayOfWeek}`}
                                    name={`daysOfWeek.${idx}.dayOfWeek`}
                                    value={day.dayOfWeek}
                                    checked={day.checked}
                                    label={t(`weekdays.${day.dayOfWeek.toLowerCase()}`, {
                                      context: 'short',
                                    })}
                                    onChange={e =&gt; {
                                      replace(idx, { ...day, checked: !day.checked })
                                    }}
                                  /&gt;
                                &lt;/Grid&gt;
                              ))}
                            &lt;/Grid&gt;
                          )}
                        /&gt;
                      &lt;/Grid&gt;
  
                      &lt;FieldArray
                        name="taskSchedules"
                        render={({ remove, replace, push }) =&gt; (
                          &lt;&gt;
                            &lt;Grid item&gt;
                              &lt;Typography variant="subtitle1" gutterBottom&gt;
                                {t('schedule_plural').toUpperCase()}
                              &lt;/Typography&gt;
  
                              {values.taskSchedules.map((schedule, idx) =&gt; (
                                &lt;Grid
                                  container
                                  spacing={6}
                                  key={`task-weekday-schedule-${schedule.uuid}`}
                                &gt;
                                  &lt;Grid item&gt;
                                    &lt;TextField
                                      type="time"
                                      key={`task-schedule-${schedule.uuid}`}
                                      id={`task-schedule-${schedule.uuid}`}
                                      label={t('time')}
                                      name={`taskSchedules.${idx}.time`}
                                      value={schedule.time}
                                      onChange={e =&gt; {
                                        const time = e.target.value
                                        replace(idx, { ...schedule, time })
                                      }}
                                      inputProps={{
                                        step: 300, // 5 min
                                      }}
                                    /&gt;
                                  &lt;/Grid&gt;
                                  &lt;Grid item&gt;
                                    &lt;RemoveScheduleButton
                                      id={`task-weekday-button-remove-schedule-${schedule.uuid}`}
                                      vanilla
                                      icon
                                      onClick={(): void =&gt; {
                                        remove(idx)
                                        // bug in formik makes this necessary
                                        // see https://github.com/jaredpalmer/formik/issues/784#issuecomment-503135849
                                        setTimeout(() =&gt; {
                                          validateForm()
                                        }, 10)
                                      }}
                                    &gt;
                                      &lt;CloseIcon /&gt;
                                    &lt;/RemoveScheduleButton&gt;
                                  &lt;/Grid&gt;
                                &lt;/Grid&gt;
                              ))}
                            &lt;/Grid&gt;
                            &lt;Grid item&gt;
                              &lt;Button
                                id="task-weekday-button-add-schedule"
                                ghost
                                onClick={(): void =&gt; {
                                  push({
                                    time: '00:00:00',
                                    taskUuid: task.uuid,
                                    uuid: uuid(),
                                    dayOfWeek: Weekday.Monday, // temporary value
                                  })
                                }}
                              &gt;
                                {t('goals:diet.task.add-schedule')}
                              &lt;/Button&gt;
                            &lt;/Grid&gt;
                          &lt;/&gt;
                        )}
                      /&gt;
  
                      &lt;Grid item&gt;
                        &lt;Grid container justify="flex-end" spacing={2}&gt;
                          &lt;Grid item&gt;
                            &lt;Button
                              id="task-button-cancel-editing"
                              size="small"
                              ghost
                              licorice
                              onClick={
                                viewMode === TaskItemViewMode.Create
                                  ? onDeleteTask
                                  : (): void =&gt; {
                                      handleReset()
                                      onChangeViewMode(TaskItemViewMode.View)
                                    }
                              }
                            &gt;
                              {t('cancel')}
                            &lt;/Button&gt;
                          &lt;/Grid&gt;
                          &lt;Grid item&gt;
                            &lt;Button
                              type="submit"
                              id="task-button-save-editing"
                              size="small"
                              disabled={!dirty || !isValid || isSubmitting}
                            &gt;
                              {t('ok')}
                            &lt;/Button&gt;
                          &lt;/Grid&gt;
                        &lt;/Grid&gt;
                      &lt;/Grid&gt;
                    &lt;/Grid&gt;
                  &lt;/Form&gt;
                )}
              &lt;/Formik&gt;
            &lt;/EditCardContent&gt;
          &lt;/Card&gt;
        )}
      &lt;/&gt;
    )
  }
  </code></pre>