formik icon indicating copy to clipboard operation
formik copied to clipboard

FieldArray slow on large data

Open totaland opened this issue 5 years ago • 17 comments

❓Question

I have a form that renders 1000 checkboxes. I use FieldArray to do it however the form getting a very slow response after clicking on the checkbox. What can I do to improve the performance and make it smoother and not feel cranky? Many thanks

image

totaland avatar Feb 19 '20 04:02 totaland

@jaredpalmer any suggestions mate?

totaland avatar Feb 20 '20 00:02 totaland

Hey, I find out how to do that. @jaredpalmer it will reduce the lag of the form even if you use 1mil checkboxes. Inside the FieldArray you implement the react-window. You can either use a react-window or build one yourself.

totaland avatar Apr 03 '20 12:04 totaland

is this fixed?

sibelius avatar Sep 04 '20 14:09 sibelius

No, it is the same, just use the workaround above

totaland avatar Sep 06 '20 22:09 totaland

can you show us the workaround code @totaland ? thanks

alexandreh92 avatar Sep 28 '20 23:09 alexandreh92

In your form you render the FieldArray item:

{<FieldArray name={'searchResult'} render={ListResult} />}

Then you have this ListResult like this:

import {FixedSizeList as List} from 'react-window';
const ListResult = () => {
    return (
      <List
        height={300}
        itemCount={length} // this can be any number of items 1mil maybe
        itemSize={30}
        width={'100%'}
      >
        {Row}
      </List>
    );
  };

const Row = React.memo(() => {return <YourActualComponent />}

From memory, you can't replace Row with React component, it only works with a function expression or arrow function, as Row like above code.

totaland avatar Sep 29 '20 00:09 totaland

In my case, this workaround isn't particularly useful. Although the data is a simple array of objects, beside checkboxes, I render input fields, dropdowns etc, which means rerendering on every keystroke is expensive and I notice significant lag for ~20 components (which is even worse on an older PC, obviously). It sucks because some libraries (like prime-react) just won't work in an uncontrolled manner, which means you are pretty much stuck with (a library such as) Formik. Using FastField doesn't make much of a difference either. The only thing that helps reduce the lag is disabling validation on blur and on change (inside <Formik>), but that's not an optimal solution. Please fix this as soon as possible! Thanks!

hstevanoski avatar Sep 29 '20 15:09 hstevanoski

My form has all of the above that you mentioned too. Do you have a codesandbox? I can have a look if I can make it better.

totaland avatar Sep 29 '20 21:09 totaland

My form has all of the above that you mentioned too. Do you have a codesandbox? I can have a look if I can make it better.

Thank you very much!!! Unfortunately, since it's not a private project, I can't share the code. All I can say is that it's a somewhat complicated form.

hstevanoski avatar Oct 02 '20 10:10 hstevanoski

Any updates on his one? I am facing the same lag issue with multiple form fields inside the field array component.

sidwebworks avatar Jun 05 '21 23:06 sidwebworks

Hey all, I'm following the progress of this on #1895 , which seems to be a similar issue

karanaditya993 avatar Jun 24 '21 19:06 karanaditya993

Encountered the same issue while dealing with form with large number of form fields. I solved it with this workaround.

  const FastTextField: FC<IProps> = ({ name, handleChange, value }) => {
    const [localValue, setLocalValue] = useState("");
  
    useEffect(() => {
      value && setLocalValue(value);
    }, [value]);
  
    const debouncedValue = useDebounce(localValue, 200);
  
    useEffect(() => {
      handleChange(localValue);
    }, [debouncedValue]);
  
    return (
      <TextField
        name={name}
        value={localValue}
        onChange={(e: any) => setLocalValue(e.target.value)}
        type="text"
      />
    );

And use this fast field something like this

 <FieldArray name="person">
    {({ push, remove }) =>
      formik.values.person.map((item, idx) => {
        const errors = formik.errors.person[idx];
        const touched = formik.touched.person[idx];
        return (
          <div key={["person", idx].join("_")}>
            <FastTextField
              name={`person[${idx}]`}
              error={touched && errors}
              value={item}
              handleChange={(value) => formik.setFieldValue( `person[${idx}]`,value )}
            />
         </div>
        )
      })
    }
  </FieldArray>

sudipstha08 avatar Aug 26 '21 11:08 sudipstha08

Only add

validateOnChange={false}

to Formik tag

remoideas avatar Jan 19 '22 03:01 remoideas

Thanks remoideas, it setting validateOnChange=false did the trick for me

nwabueze1 avatar Sep 16 '22 14:09 nwabueze1

import { FieldArray, FastField } from 'formik';

Replace the standard formik <Field> component with the <FastField> component, as it will minimize the number of re-renders and improve performance.

arturowin avatar Jan 30 '23 14:01 arturowin

Only add

validateOnChange={false}

to Formik tag

First it takes 16 second for rendering but now it just takes the 8 second for a rendering thank you for this awsome solution 😇😇

Sanketkondhalkar avatar Apr 22 '24 17:04 Sanketkondhalkar

  1. validateOnChange={false}
  2. Turn what's inside map function into a component. Pass props as you need.
  3. Wrap the component with memo()

These steps give me almost zero lagging time.

ChayaninSuatap avatar Apr 26 '24 09:04 ChayaninSuatap