jaredpalmer / formik

Build forms in React, without the tears 😭
https://formik.org
Apache License 2.0
33.98k stars 2.79k forks source link

FieldArray slow on large data #2296

Open totaland opened 4 years ago

totaland commented 4 years ago

❓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 commented 4 years ago

@jaredpalmer any suggestions mate?

totaland commented 4 years ago

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.

sibelius commented 4 years ago

is this fixed?

totaland commented 4 years ago

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

alexandreh92 commented 4 years ago

can you show us the workaround code @totaland ? thanks

totaland commented 4 years ago

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.

hstevanoski commented 4 years ago

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!

totaland commented 4 years ago

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.

hstevanoski commented 4 years ago

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.

sidwebworks commented 3 years ago

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

karanaditya993 commented 3 years ago

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

sudipstha08 commented 3 years ago

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>
remoideas commented 2 years ago

Only add

validateOnChange={false}

to Formik tag

nwabueze1 commented 2 years ago

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

arturowin commented 1 year ago

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.

Sanketkondhalkar commented 6 months ago

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 😇😇

ChayaninSuatap commented 6 months ago
  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.