
通过分离渲染模板与表单状态、将单行状态下沉至 Row 组件、配合 useReducer + dispatch 稳定引用及 React.memo,可确保仅点击的 RadioButton 所在行重渲染,大幅提升百行级表格性能。
通过分离渲染模板与表单状态、将单行状态下沉至 row 组件、配合 `usereducer` + `dispatch` 稳定引用及 `react.memo`,可确保仅点击的 radiobutton 所在行重渲染,大幅提升百行级表格性能。
在构建高交互性 Survey 类应用(如羊绒品质评分系统)时,表格中数十个 Radio Button 的响应性能极易成为瓶颈。原始实现中,每次点击任一按钮都会触发整个
要实现「只重渲染被操作的行」,核心策略是 状态下放 + 引用稳定 + 智能 memo,具体分三步落地:
✅ 1. 分离「渲染结构」与「表单数据」
定义只读的 ScorecardTemplate(含 attributes, possibleValues 等静态配置),与动态的 formValues 状态解耦。模板不随用户输入变化,因此可安全作为 memo 的依赖或直接传入子组件,避免因数据变更引发非必要重渲染。
✅ 2. 将单行状态下沉至 MultipleChoiceGridRow
关键优化点:每行维护自己的 useState
- 点击某行 RadioButton 仅触发该行 setActualValue → 仅该行组件重渲染;
- 其他行无状态变化,且 props(rowName, possibleValues)未变,React.memo 自动跳过渲染;
- 控制台日志 +++ rendering row X 可清晰验证仅目标行触发。
const MultipleChoiceGridRow: React.FC<multiplechoicegridrowprops> = ({
rowName,
possibleValues,
onValueChange
}) => {
const [actualValue, setActualValue] = useState<string>("");
const handlePress = (value: string) => {
setActualValue(value);
onValueChange(value); // 向上透传变更
};
return (
<datatable.row><datatable.cell><text>{rowName}</text></datatable.cell>
{possibleValues.map(val => (
<datatable.cell key="{val}"><radiobutton status="{actualValue" val : onpress="{()"> handlePress(val)}
value={val}
/>
</radiobutton></datatable.cell>
))}
</datatable.row>
);
};</string></multiplechoicegridrowprops>
✅ 3. 使用 useReducer + dispatch 实现稳定回调链
顶层使用 useReducer 管理全局表单值,并将 dispatch 函数向下透传(而非 onTableChange 回调)。由于 React 保证 dispatch 引用恒定,可安全用于 React.memo 的依赖判断:
const MultipleChoiceGrid = React.memo(({
category,
dispatch,
template
}: MultipleChoiceGridProps) => {
const handleUpdate = (attr: string, val: string) => {
dispatch({
type: "update",
payload: { category, subcategory: template.name, attribute: attr, value: val }
});
};
return (
<datatable><multiplechoicegridheader possiblevalues="{template.possibleValues}"></multiplechoicegridheader><multiplechoicegridrows attributes="{template.attributes}" possiblevalues="{template.possibleValues}" onattributevaluechange="{handleUpdate}"></multiplechoicegridrows></datatable>
);
});
⚠️ 注意事项:
- 勿在 map 中排序原数组:原始代码 rows.scores.sort(...) 会直接修改原数组,破坏不可变性。应使用 [...rows.scores].sort(...);
- key 必须唯一且稳定:行级 key={attribute.name}、单元格 key={value} 避免因顺序变化导致错误复用;
- 避免 useCallback 过度包裹:当回调仅用于透传 dispatch 时,无需 useCallback —— dispatch 本身已稳定;
- Surface 等容器组件也建议 memo:若其子组件已优化,外层容器亦可 memo 防止冗余布局计算。
最终效果:100+ 行表格中点击任意 RadioButton,仅对应











