callstack / callstack/react-native-paper

Performance: theme color computations run on every render in List.Item, TextInput, Chip, Divider, Appbar.Action

未关闭
#4,946 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
bug
主要语言
TypeScript
星标
14.5k
派生
2.2k
平均合并
5 天 23 小时
30 天内合并 PR
12

描述

### Current behaviour

Several theme-derived colors in commonly-rendered components are recomputed via the color package (color(...).alpha(...).rgb().string()) on every render, even when their inputs don't change across renders. The result is wasted CPU on every keystroke in TextInput, on every parent re-render of any list of List.Item / Chip / Divider, and on every render of Appbar.Action. We noticed it while profiling typing latency on a search screen and scroll/interaction jank on a settings list, where calls into the color package showed up much more frequently than expected in flamegraphs.

The affected sites we found (all in the v2 theme path or other input-independent branches):

- [src/components/Divider.tsx:67-70](vscode-webview://104p3er6308m39fmlbmbq03n8jkb5u26aetvdm327jqvb2cklg06/src/components/Divider.tsx#L67-L70) — color(isDarkTheme ? white : black).alpha(0.12).rgb().string(). The inputs are constants plus a boolean; there are exactly two possible results, but we recompute on every render of every divider.

- [src/components/List/ListItem.tsx:209-211](vscode-webview://104p3er6308m39fmlbmbq03n8jkb5u26aetvdm327jqvb2cklg06/src/components/List/ListItem.tsx#L209-L211) and [233-235](vscode-webview://104p3er6308m39fmlbmbq03n8jkb5u26aetvdm327jqvb2cklg06/src/components/List/ListItem.tsx#L233-L235) — titleColor and descriptionColor run a full color() chain on every render. List.Item is rendered N times in lists, so the cost multiplies.

- [src/components/Appbar/AppbarAction.tsx:108](vscode-webview://104p3er6308m39fmlbmbq03n8jkb5u26aetvdm327jqvb2cklg06/src/components/Appbar/AppbarAction.tsx#L108) — color(black).alpha(0.54).rgb().string(). This branch has no dependencies at all; both arguments are constants, yet the chain runs on every render.

- [src/components/TextInput/helpers.tsx:332, 367, 401, 410, 421-422](vscode-webview://104p3er6308m39fmlbmbq03n8jkb5u26aetvdm327jqvb2cklg06/src/components/TextInput/helpers.tsx) — getTextColor, getActiveColor, getSelectionColor, getFlatBackgroundColor each contain color() chains and are called on every TextInput render. Since TextInput re-renders on every keystroke, this fires constantly during typing.

- [src/components/Chip/helpers.tsx:34, 38, 46, 50, 53, 89, 92, 135](vscode-webview://104p3er6308m39fmlbmbq03n8jkb5u26aetvdm327jqvb2cklg06/src/components/Chip/helpers.tsx) — getBorderColor, getTextColor, getBackgroundColor each contain multiple color().alpha().rgb().string() branches and run per Chip per render. A filter strip with N chips multiplies this by N on every parent re-render.

Individually each call is cheap, but the color package parses + allocates + serializes on each invocation, and these are all in render paths that fire either per-keystroke or per-list-item.
### Expected behaviour

Theme-derived colors that don't change between renders should be computed once (module-level constant when the inputs are constants, or useMemo keyed on the actual dependencies) rather than on every render.

Concretely:

- Divider v2 branch → two module-level constants (DARK_DIVIDER_COLOR, LIGHT_DIVIDER_COLOR), pick between them.

- Appbar.Action v2 fallback → module-level constant for color(black).alpha(0.54).rgb().string().

- List.Item titleColor / descriptionColor → useMemo keyed on theme.colors.text and theme.isV3.

- TextInput helpers → memoize the helper results at the call site (keyed on theme, disabled, error, mode, custom colors), or cache by argument tuple inside the helpers.

- Chip helpers → one useMemo in Chip for the three derived colors, keyed on theme, isOutlined, disabled, selectedColor.

No behavior change, no API change — purely moving constants out of render scope and adding useMemo where dependencies are stable.
### How to reproduce?

This isn't a visual bug, so the "repro" is profiling rather than a screenshot. To observe it directly:

In any app using react-native-paper, open a screen with a TextInput from the library.
Start the React DevTools Profiler (or use Flipper / Hermes sampling profiler) and record while typing a few characters.
Inspect the flame graph for the TextInput commit cycle — calls into the color package (alpha, rgb, string) appear on every keystroke from getTextColor, getActiveColor, getSelectionColor, and (in flat mode) getFlatBackgroundColor.
Same pattern reproduces with a list of ~30 List.Items, or a FlatList of Chips, by triggering a parent re-render (e.g. toggling a selection) and recording the commit.

A minimal reproduction isn't strictly necessary here since the issue is visible by reading the source at the linked line numbers — happy to put one together on Snack if it would help maintainers.

### Preview

N/A — profiler-observable, not visually observable. Can attach a flamegraph screenshot if maintainers want one.

### What have you tried so far?
What have you tried so far?
Patched the Divider and Appbar.Action v2 branches locally to use module-level constants — verified via the profiler that the color calls disappear from those components' commits, no visual regression.
Wrapped titleColor / descriptionColor in List.Item in useMemo keyed on theme.colors.text and theme.isV3 — same result, no regression.
For TextInput, memoizing the four helper results at the call site removed the per-keystroke color calls in the profile.
All changes are local and mechanical; happy to send a PR (combined or split per component, whichever the maintainers prefer).

### Your Environment

| software | version
| --------------------- | -------
| ios | 18
| android | x
| react-native | 0.82.1
| react-native-paper | 5.15.2
| node | 22.x.x
| npm or yarn | 11.12.1
| expo sdk | 53

贡献指南

打开贡献指南

调研方向

首先检查 src/components/Divider.tsx、src/components/List/ListItem.tsx、src/components/Appbar/AppbarAction.tsx、src/components/TextInput/helpers.tsx 和 src/components/Chip/helpers.tsx 中受影响的渲染路径。比较输入和重新渲染列表时更改前后的 profiler 行为;当从 theme 派生的稳定颜色不再触发重复的颜色计算,且没有视觉或 API 更改时,即表示完成。

由索引模型根据 Issue 内容生成。

评估

技术栈
react-native, typescript
领域
mobile, performance
Issue 类型
重构
难度
4/5
预计耗时
3-5 天
活跃度
冷清
描述清晰度
描述清楚
新手友好度
48/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。