Open AlanKrygowski opened 5 years ago
Not sure whether bumping things here is punishable by death....but the issue still exists, and really is the only thing blocking me from convincing my peers to switch our form handling to formik :L
@AlanKrygowski - I was able to solve this re-render issue in a slightly unorthodox way. The issue with your code sandbox example - and with the form I was working on is that you've effectively got a piece of state in Formik that controls your entries. Then you've got your <FieldArray>
component and inside that you're mapping your entries to render the various form fields. The issue is that these fields then set their own state on entries - i.e you've got:
const initialValues = {
entries: [],
}
const EntryItem = ({namePrefix}) => <Field name={`${namePrefix}.name`} component={input} />
const MyForm = () => {
return(
{/*}...Left out for brevity...*/}
<FieldArray>
{arrayHelpers => (
<>
{values.entries.map((_, idx) => (
<EntryItem
key={idx}
namePrefix={`entries.${idx}`}
/>
))}
<Button onClick={() => arrayHelpers.push("Placeholder String")}>
Add Entry
</Button>
</>
)}
</FieldArray>
);
}
Each time you type into <EntryItem />
you're triggering an update to values.entries.whatever
which is causing the map to fire again and re-rendering the component.
How I solved the issue is to do the following (NB: this is just example code - actual has a lot more boilerplate):
const initialValues = {
entries: [],
entryValues: {}
}
const EntryItem = React.memo(({namePrefix}) => <Field name={`${namePrefix}.name`} component={input} />);
const MyForm = () => {
return(
{/*}...Left out for brevity...*/}
<FieldArray>
{arrayHelpers => (
<>
{values.entries.map((entry) => (
<EntryItem
key={entry}
namePrefix={`entryValues.${entry}`}
/>
))}
<Button onClick={() => {
arrayHelpers.push(values.entries[values.entries.length - 1] + 1 || 0); // This gets the last value in the array and adds 1 to prevent duplicate keys. If there are no items in the array it initializes it at 0.
setFieldValue("entryValues", {
...values.entryValues,
[values.entries.length]: "Placeholder String"
});
}}>
Add Entry
</Button>
</>
)}
</FieldArray>
);
}
So what's happening here is you're using the entries array as a place to store keys (based on the length of your entries array) for values which are stored on a separate object. Think of it as being similar to normalized state in Redux i.e. allIds, byId.
Because you're updating the values inside entryValues (which is not being mapped) the components are not re-rendered (the memo helps ensure this) except when adding to or deleting from the entries array.
You do have to do a bit of work if you want to add the ability to remove entries from the field array as well - but that basically involves passing the arrayHelpers and setFieldValue down into the EntryItem and updating the value of entryValues and your array of key indexes to remove any unneeded values.
Hope that helps!
Quick edit to cover removals - removing items from the values object is tricky - best I've come up with so far is to set the values on the keys as undefined.
I've got the same issue, every FastField in my form is fairly fast except for inside of FieldArray
Hello! First and foremost, thank you for all the hard work on formik, it is a great library that I've used often and really appreciate it's ease of use. Wondering if this is being worked on or if there is an ETA on the implementation? Running into similar issues where <FieldArray />
does not utilize the performance optimization of <FastField />
, especially within the context of large forms (i.e. 30+ fields). Thanks very much.
👋 I've been experiencing this issue also. We get a lot of re-rendering with FieldArray causing slowness. I decided to take a look at the code. It seems to re-render regardless of the child component's state. I've added a PR which addresses this, it adds the same functionality as FastField
i.e you can now pass a shouldUpdate
method to it which controls when it should re-render. If not it will check changes between the props.
For now ive added the functionality to the FieldArray
but it may need to be a new FastFieldArray
depending on what the maintainers here think.
🐛 Bug report
Current Behavior
I have a
FastField
andField
group inside a component wrapped inside aFieldArray
. Every change I make to any of the Fields causes rerenders in all of the fields (Including theFastFields
.Expected behavior
Making changes in the
FastField
should not trigger re-renders in the Fields and other FastFields.Reproducible example
Here is prototype example where the issue can be seen https://codesandbox.io/s/distracted-hamilton-uf6cb?fontsize=14.
The re-renders were found when opening the page in a fullscreen tab, and checking them with the React Dev Tools. Here's a screencast of the rerenders: https://drive.google.com/file/d/1ydVG0U8rAhzzzec0tv7k6M-X4qJlHY2B/view
The top right input for each array element and the textarea inputs are the mentioned FastFields
Additional context
The form I'm working on is a rather big form. The example here is a rather basic version of it. The form is based around entries that are each stored inside the
FieldArray
.Your environment
The ENV is a standard codesandbox enviorment with formik added as a dependency. All is the most up to date, since the prototype was created two days ago.
Sidenote
I wasn't able to find any information about this but: Is there a way to ensure the individual fields of the
FieldArray
component would not trigger re-rendering of the the other field array fields?