Every MessageRow read the store for itself: matrixPermissions, smilieIndex, bufferEmoji, pinnedMessages, the buffer list (to know
whether this is a direct message) and the irc.renderColours preference. Six
subscriptions per row. A conversation with two thousand rows on screen
therefore held twelve thousand live subscriptions, and every one of their
selectors re-ran on every store change anywhere in the client.
Nothing in src/renderer/src/components/ was wrapped in React.memo - not
one file - and MessageList subscribes to the whole messagesByBuffer map.
That map's identity changes on every message in any of 485 buffers, so a
message arriving in a channel nobody was looking at redrew every row of the
conversation that was.
This is the half of the report that was about the client feeling slow rather
than the client being large.
The solution
The things every row in a conversation reads alike - its emoji, what this
account may do in it, which of its messages are pinned, whether it is a direct
message, whether IRC colours are drawn - are properties of the conversation
and not of the message. They are read once by whoever draws the list, through
a useRowContext(bufferId) hook, and handed down as one memoised object. ThreadPanel uses the same hook for the same reason.
With that gone from the rows, the rows are React.memo'd: every remaining
prop is either a value or a reference held steady by the list, so a row
redraws when its own message does.
Fixed in 46e3d7f "Draw the part of the log somebody is reading".
## The problem
Every `MessageRow` read the store for itself: `matrixPermissions`,
`smilieIndex`, `bufferEmoji`, `pinnedMessages`, the buffer list (to know
whether this is a direct message) and the `irc.renderColours` preference. Six
subscriptions per row. A conversation with two thousand rows on screen
therefore held **twelve thousand live subscriptions**, and every one of their
selectors re-ran on every store change anywhere in the client.
Nothing in `src/renderer/src/components/` was wrapped in `React.memo` - not
one file - and `MessageList` subscribes to the whole `messagesByBuffer` map.
That map's identity changes on every message in any of 485 buffers, so a
message arriving in a channel nobody was looking at redrew every row of the
conversation that was.
This is the half of the report that was about the client feeling slow rather
than the client being large.
## The solution
The things every row in a conversation reads alike - its emoji, what this
account may do in it, which of its messages are pinned, whether it is a direct
message, whether IRC colours are drawn - are properties of the conversation
and not of the message. They are read once by whoever draws the list, through
a `useRowContext(bufferId)` hook, and handed down as one memoised object.
`ThreadPanel` uses the same hook for the same reason.
With that gone from the rows, the rows are `React.memo`'d: every remaining
prop is either a value or a reference held steady by the list, so a row
redraws when its own message does.
Fixed in 46e3d7f "Draw the part of the log somebody is reading".
Salastil
added the bug label 2026-09-07 17:52:35 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The problem
Every
MessageRowread the store for itself:matrixPermissions,smilieIndex,bufferEmoji,pinnedMessages, the buffer list (to knowwhether this is a direct message) and the
irc.renderColourspreference. Sixsubscriptions per row. A conversation with two thousand rows on screen
therefore held twelve thousand live subscriptions, and every one of their
selectors re-ran on every store change anywhere in the client.
Nothing in
src/renderer/src/components/was wrapped inReact.memo- notone file - and
MessageListsubscribes to the wholemessagesByBuffermap.That map's identity changes on every message in any of 485 buffers, so a
message arriving in a channel nobody was looking at redrew every row of the
conversation that was.
This is the half of the report that was about the client feeling slow rather
than the client being large.
The solution
The things every row in a conversation reads alike - its emoji, what this
account may do in it, which of its messages are pinned, whether it is a direct
message, whether IRC colours are drawn - are properties of the conversation
and not of the message. They are read once by whoever draws the list, through
a
useRowContext(bufferId)hook, and handed down as one memoised object.ThreadPaneluses the same hook for the same reason.With that gone from the rows, the rows are
React.memo'd: every remainingprop is either a value or a reference held steady by the list, so a row
redraws when its own message does.
Fixed in
46e3d7f"Draw the part of the log somebody is reading".Finished -
46e3d7f953