How to improve the performance of multiple TextInput in a parent component?
See original GitHub issueSo I was designing a form that allows users to dynamically add/remove entries, where an entry contains a Select
and TextInput
component:
The naive implementation is to create a parent component that keeps the state of each entry and passing the required states and several callbacks as props to the children component, so that the parent can control the children and track the state change. However, the performance is bad with just around ten entries created. Input is slightly lagged, and you won’t see characters coming out one-by-one when holding a key. It just freezes when a key is pressed, and then a bunch of characters coming up after released the key.
Here’s the code example. Click Add icon to add more entries: https://codesandbox.io/s/dynamic-input-state-in-parent-qy9pc?file=/src/App.js
I have another design that keeps the state of TextInput
and Select
inside the children component. The parent component uses an instance variable instead of state to track children’s state, so that children state changes won’t trigger rendering. This design does improve the performance, but I’m just a few months into React so I’m not sure this the correct way to use React.
I’m wondering how can I improve the performance? The performance is acceptable when using pure React (with only div
, input
, button
…).
Issue Analytics
- State:
- Created 3 years ago
- Comments:10 (3 by maintainers)
Top GitHub Comments
Update: I have been able to recreate the issue and have also confirmed @IanKBovard’s suspicion that the re-renders are being caused by components consuming FormContext.
Using React Dev Tool’s Profiler, we can see that when a input value is updated, two commits result:
When evaluating the side effects commit, we see that a changed input value causes each of the
<Select />
and<TextInput />
contained in the<Form />
to re-render. We can also see that @johnliu55tw’s memoization of the dynamic entry component is effective, preventing re-renders of that component, but not it’s Select and TextInput children.Additionally, the render time scales linearly with each additional input added, which is why we see visible performance degradation as 6+ of the dynamic form entries being added.
<Select />
renders are ~4X more costly than<TextInput />
, so I am focussing on Select for the rest of this update.When inspecting what is causing
<Select />
to render, we see that is because its Hooks changed.Unfortunately, React Dev Tools does not currently have a mechanism to identify which hooks have changed. That is because hooks are currently anonymous. I have, however, been able to isolate the utilization of FormContext. When excluded from Select, we do not see Selects re-rendering; when included, all Selects re-render. So, the consumption of FormContext should be the focus for optimization.
The effect of these re-renders is amplified when
<TextInput />
is interacted with because each keystroke is setting an updated value in FormContext.I am exploring a few different patterns to address, but have not yet gotten to a working solution. I have begun exploring the options Dan lists here, including:
React.memo
/React.useMemo
)A fourth approach to explore could be this strategy.
Thank you for the follow up @johnliu55tw. I will attempt to recreate using your approach. Appreciate you providing the confirmation and detail.