Some takeaways from another performance troubleshooting thread:
- Take advantage of component boundaries.
-
Components will not re-render if new state is referentially equal to previous state.
This feature is nice to use in child components if the input received from the parent is the entire state of the child. In the below block, the child will not re-render if the parent has not modified the input.
While still technically true, it’s better to just use a function returningHH.HTMLwrapped inHH.lazyinstead of a child component. -
Manually prevent unnecessary state updates. (when appropriate)
If the component’s state will be considered updated by applying new identical data, (e.g. overwriting a record field to produce a new record reference), then an unnecessary re-render will be triggered. You can prevent this re-render by manually checking for differences before updating withH.modifyorH.put. Note that comparisons are different for references versus values and types with anEqinstance.
handleAction = case _ of
HandleNewValue val -> do
old <- H.get
when (old.val /= val) $ H.modify_ _ { val = val }
HandleNewReference ref -> do
old <- H.get
unless (unsafeRefEq old.ref ref) $ H.modify_ _ { ref = ref }
-
Use HH.lazy and HH.memoized (when appropriate)
Avoid re-running functions that generateHH.HTML, if their arguments are the same as before. The Elm guide and this blog post provide a good explanation of how this works. Differences betweenHH.lazyandHH.memoizedare that lazy compares references, while memoized lets you provide a custom comparison function. lazy also supports more than one function argument (multiple args to memoized may be wrapped in a record). More details on usage are in the Halogen source. -
Use keyed elements for lists where items are inserted, removed, or reordered
Again, the Elm guide describes this well. See Halogen.HTML.Elements.Keyed for the PureScript equivalent. Note that using keyed elements may result in worse performance. For example, this keyed table performs much worse than the non-keyed equivalent. -
Use browser debugger to check if render code is being run unnecessarily.
It’s tough (or impossible) to detect renders in PS with logging, butyou can find your component’s render code in the JS files and set a breakpoint there to catch any unnecessary re-renders.
Edit: Debug.Trace is another option for tracking program flow. -
Balance cost of checking for equality against cost of re-rendering.