Optimizing render performance - support for React.memo and key prop?

In complex views of our app, we have loads of components and elements being rendered on the page. E.g. a form that has dozens of sections and fields. Or, a table that has ~100-200 rows.

Unfortunately, in one of our Plasmic apps, we’ve reached a point where some of our pages are that complex — and because of that, we can’t afford rerendering everything whenever user interacts with the page. (As a simple click event blocks our UI now for as long as 0.5-1.5s.)

The solution to that is to decrease the number of components that get rerendered at once. For example, if a row gets selected/deselected, only that row should be rerendered. React shouldn’t need to completely rerender all of the remaining rows. Or, if a form field gets changed, only section related to it should rerender. React shouldn’t need to rerender the whole form.

In the normal React development, we’d add key={...} property to the row component. And wrap a few of our components with React.memo, so they don’t rerender when not needed. Such optimizations would usually be enough to make the whole complex view render quick and nicely.

How can we do that in Plasmic? I’m afraid currently that’s not possible. Do you have any plans for this? So that we can optimize the Plasmic-generated code where needed, so that the interface feels responsive to the clients? For example:

  • There could be a switch allowing to turn on/off React.memo for a certain component.
  • When elements are repeated, there could be additional “collection key” attribute used for the key={...} React attribute. ( Rendering Lists – React )

If you don’t plan any of that, what workarounds to this problem do you recommend? Besides of course asking our designers to redesign the app, so that the views are less complex.

Hello @jacek_tomaszewski, if you are using codegen, this should currently be possible if you wrap the wrapper components generated by Plasmic with React.memo and compose your pages in codegen with those wrappers.

Regarding repeat element keys, we currently use index as a key when you apply repeat element on a node in Studio. To use a better key than index, you can perhaps use prop overrides to iterate over items and provide a custom key.

Yeah, we’ll probably add a bit of React.memo here-and-there.

But before we do that, one more thing worries me now - the DataProvider’s context. How does $ctx behave under the hood? Do any changes to the $ctx cause all nested children to be rerendered? If yes, then React.memo won’t help us at all? :frowning: (!)

Instead, I’d be expecting that when a child is “dependant” on a specific context property, e.g. $ctx.auth, then that child is rerendered only when $ctx.auth is updated (and not when e.g. $ctx.somethingElse is updated). Does it work like that?

By looking at plasmic host source code, I’m afraid not:

I’m afraid it means that whenever my component which mounts a DataProvider gets rerendered, all of its’ nested nodes will also be rerendered (as every single Plasmic-generated component has a useDataEnv() call in it).

Because unfortunately, every Plasmic-generated component listens to the context by calling useContext(DataContext), even though it doesn’t use that context at all:

Am I right?

If so, do you have plans to improve it in the nearest future? E.g. so that a component listens only to the changes of specific properties of the context (the ones it actually uses). And so that it doesn’t listen to the context at all, if it’s not used.

Otherwise I’m afraid we’ll either have to write our own implementation of DataProvider and switch to it everywhere… which I don’t even know if it’s possible. Or, switch away from Plasmic for complex views… which isn’t something we’d like to do, as Plasmic-based development so far has been sufficient for us everywhere.

EDIT: Just to be sure, I just confirmed I’m right on this. Covering a child component with useMemo(() => ..., []) doesn’t do much, as it will be rerendered anyways whenever it’s contained in any DataProvider component. Even, if the value of that DataProvider doesn’t change at all. And because DataProvider is central to Plasmic development (if we want to define “memoized variables” inside of a component, or use e.g. API providers, they use DataProvider under the hood.), I’m afraid there’s no workaround here for us.

Hi Jacek, thanks so much for this valuable feedback. We are currently working on improving Plasmic internals such as state management, and both the key and DataProvider issues will absolutely be high on the list.