Albert Purnama • 2022-05-15
Let me share what I learned from debugging performance issues at Typedream.

Generally, you shouldn't need to care about performance at all, since React is already extremely efficient & fast.
We haven't really solved performance and I think we will never be able to.
Performance improvement is something that will keep popping up on our radar from time to time because as new features get added, it's almost always guaranteed to be a complete shitshow on the performance metrics.
It's almost impossible to ship fast, with even good performance in the first MVP. Even the gods of software engineering that I see on Twitter still have shitty performances on their first MVP.
And this is okay. Let's dissect one of our latest performance improvement and see how you can probably benefit from this too.
You must understand that Typedream is very tightly coupled with Slate. Every time there's a change in Slate, Typedream needs to know exactly which components to re-render.
Why? Didn't you say React is fast enough?
Great question, yes React is extremely fast, but you can't bend physics/physical limitations. It is still within the boundaries of the browser's render cycle, CPU performance, etc.
I tried running a profiler on a page that has > 50 column blocks, each block has 3 columns, and each column has 3 items with nested elements inside. With approximately 300+ components rendered a single time, this is what it looks like:
Render cycle of a couple of clicks on some block elements
Those yellow spikes are exactly the renders of a single click (roughly). Those are only ~70ms, you won't really feel the lag when you're editing on the Typedream page.
Let's pause for a moment and think about it, without even considering performance in our MVP, React (with every library that we have on top of it) managed to save us from ourselves.
We've really hit performance issues when there are more nested components (100+), new features like CMS, heavy embeds, etc.
See full source here
Focus on line 55 on the right side:
const selectedIDs = useContext(ToolbarSelectedIDsContext);What does this mean?
This means that ALL SelectableElement will re-render every time the selectedID changes. And guess where this component is used? EVERYWHERE. Every single Typedream block element (whenever you see that square boxes around the element you're editing, you're looking at SelectableElement !
ToolbarSelectedIDsContext isn't even the worse thing. Since it's only used for multiple selections. What's worse is:
See full source here
Same case, but this means everytime we open toolbar, or click an element. EVERYTHING RERENDERS
This means more components = less performance.
There's no easy solution. We still have to rewrite resource-hungry components and break down context into proper state management.
Introducing Zustand
Credits go to Armedi for suggesting the use of Zustand for a layer of state management. It's simple and easy to use & understand.
Zustand is just like any other state management tool out there, but with simpler APIs. Zustand solves the problem described above by replacing
const leftMenuPath = useContext(ToolbarPathContext);const isCurrentPath = useMemo( () => isEqual(leftMenuPath, path), [leftMenuPath, path],);Into
const isCurrentPath = useStore( (state) => isEqual(state.path, path),);What does this do?
Simple, rather than asking ToolbarPathContext what the currently selected path is, we simply provide the store with some callback function that returns a boolean instead of the path itself.
This way, the component doesn't re-render when this boolean value doesn't change.
General Pattern
The pattern that I think we should go for is more heavyweight store usage rather than internal component calculation. In the case above, you're doing isEqual inside the store itself, so the component just receives whatever value the store spits out.
Should I really think about this when I develop new components?
I'd say no to most components.
If components you're developing aren't used much, you're probably better off using React's context or even drilling the props(not recommended).
Let's face it, nobody likes state management, they can sometimes be really complex and really opinionated. We should really stay away from implementing additional layers unless we're experiencing problems.
With that said, it's also bad to straight-up neglect state management as it directly affects performance.
If you know for sure that your component will be used quite often, then spend a bit of time to stress test the component, try rendering 20, 50, or 100 of them on a single page and see how well it performs on the editor.
State management ties with React's render cycle. Render cycles directly ties to Performance. We still haven't properly managed our state, therefore our performance kinda sucks.
Render cycles are one of the trickiest things to get right. I doubt any companies ever use React properly, I bet even some parts of Facebook still haven't properly managed their state correctly.
Don't worry or think too much about it, it will eventually come naturally the more you fix rendering issues or read my rants about our issues on the engineering slack channel ✌️
Cardy
Copyright © 2021 Govest, Inc. All rights reserved.