ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Sanity 仓库中的 React 最佳实践:用 toSorted() 替代 sort() 实现不可变排序

Sanity 仓库中的 React 最佳实践:用 toSorted() 替代 sort() 实现不可变排序 Sanity 仓库中的 React 最佳实践用 toSorted() 替代 sort() 实现不可变排序【免费下载链接】sanitySanity Studio – Rapidly configure content workspaces powered by structured content项目地址: https://gitcode.com/GitHub_Trending/sa/sanity本文基于 Sanity Studio 仓库内置的 Vercel React Best Practices 规则集深入讲解js-tosorted-immutable这一条规则为什么.sort()的原地变更mutation会破坏 React 的状态与属性模型如何用.toSorted()以零心智负担的方式返回新排序数组以及在不支持新方法的旧环境下如何用展开运算符回退。读完本文你将掌握该规则的完整判定依据、浏览器兼容边界并能在 Sanity Studio 源码中看到这一模式在真实工程中的落地形态。规则的出处Vercel React Best Practices 技能集在 Sanity 仓库中这套规则以「技能skill」形式维护供 AI Agent 与开发者在编写、审查、重构 React/Next.js 代码时引用。规则全文位于js-tosorted-immutable.md同一份内容在 skills/vercel-react-best-practices/rules/js-tosorted-immutable.md 中有一份镜像副本经比对两处内容完全一致总入口为 SKILL.md完整展开版编译文档见 AGENTS.md。该技能集共收录 57 条规则、8 个分类按影响程度impact排定优先级摘自 SKILL.md 的分类表优先级分类影响等级文件名前缀1Eliminating WaterfallsCRITICALasync-2Bundle Size OptimizationCRITICALbundle-3Server-Side PerformanceHIGHserver-4Client-Side Data FetchingMEDIUM-HIGHclient-5Re-render OptimizationMEDIUMrerender-6Rendering PerformanceMEDIUMrendering-7JavaScript PerformanceLOW-MEDIUMjs-8Advanced PatternsLOWadvanced-js-tosorted-immutable属于第 7 类 JavaScript Performance在快速索引中描述为 “Use toSorted() for immutability”。值得注意的是规则文件自身 frontmatter 的标注title: Use toSorted() Instead of sort() for Immutability impact: MEDIUM-HIGH impactDescription: prevents mutation bugs in React state tags: javascript, arrays, immutability, react, state, mutation这里存在一个有意义的差异分类层面js-前缀被定为 LOW-MEDIUM增量优化而这条规则自身标注为 MEDIUM-HIGH——因为它的目的不是提速而是「防止 React 状态中的变更mutation缺陷」属于正确性问题而非性能问题。这也是理解该规则优先级定位的关键。问题本质sort() 的原地变更破坏 React 不可变模型.sort()会直接修改原数组这对 React 的 state 和 props 是危险的因为 React 预期二者按只读处理。规则文档给出的反例function UserList({ users }: { users: User[] }) { // Mutates the users prop array! const sorted useMemo( () users.sort((a, b) a.name.localeCompare(b.name)), [users] ) return div{sorted.map(renderUser)}/div }这段代码有两层问题users.sort(...)修改了父组件传入的users数组本体。父组件持有的是同一个引用它在别的渲染中「看到」的数组顺序已被子组件悄悄改变而 React 由于引用未变可能根本不触发相关更新——这是典型的「状态被静默篡改」bug。useMemo并不能保护你。依赖数组是[users]而users.sort()就地重排后引用不变缓存命中时返回的还是被污染后的数据且副作用mutation发生在「渲染阶段」这一 React 明确不保证幂等的位置。规则给出的正确写法——用.toSorted()返回新数组原数组保持不变function UserList({ users }: { users: User[] }) { // Creates new sorted array, original unchanged const sorted useMemo( () users.toSorted((a, b) a.name.localeCompare(b.name)), [users] ) return div{sorted.map(renderUser)}/div }文档将「为什么在 React 中重要」归纳为两点值得展开Props/state 的变更破坏不可变模型React 的更新检测、useMemo/useCallback依赖比较、Object.is判断都建立在「引用变化才代表数据变化」之上。就地排序让引用不变而内容已变上述机制全部失效。闭包中的 stale closure 问题在回调、effect 中排序一个来自闭包的数组被修改的往往是外层仍持有的同一引用后续闭包再读时拿到的是意外顺序且极难复现和调试。浏览器支持边界与旧环境回退方案.toSorted()属于 ES2023 引入的一组不可变数组方法规则文档给出的支持基线是Chrome 110、Safari 16、Firefox 115、Node.js 20。在此之前的环境以及 TypeScriptlib目标尚未覆盖 ES2023 的工程应使用展开运算符回退这是文档明确推荐的兜底写法// Fallback for older browsers const sorted [...items].sort((a, b) a.value - b.value)这个回退与toSorted()语义等价先浅拷贝出一个新数组再对副本执行就地排序原始数组引用与内容都不受影响。需要留意的是浅拷贝语义——对元素对象本身不会做深拷贝这一点与toSorted()完全一致两者都只复制数组结构不复制元素。ES2023 不可变数组方法全家桶规则文档末尾列出了同族的四个不可变方法它们分别对应传统「就地修改」方法的不可变版本不可变方法对应的就地方法行为.toSorted().sort()返回排序后的新数组.toReversed().reverse()返回反转后的新数组.toSpliced().splice()返回插入/删除后的新数组.with()索引赋值arr[i] v返回替换单个元素后的新数组在 React 场景下凡是「拿到一个来自 props/state 的数组、想按某种顺序消费它」的需求都应优先选择左列方法它们把「产生新数据」的意图显式化从语法层面杜绝了误改源数据的可能。仓库实证Sanity Studio 源码中「排序即复制」的工程惯例从源码结构看Sanity Studio 核心包当前尚未直接使用toSorted()/toReversed()/toSpliced()在packages/sanity的 TS/TSX 源码中未检索到调用其既有代码普遍采用规则推荐的展开回退模式形成了可对照的实战样本sortReleases典型的「复制后排序」。Releases 功能中用于按发布类型与时间排序的工具函数先[...releases]浅拷贝再排序见 utils.tsexport function sortReleases(releases: ReleaseDocument[] []): ReleaseDocument[] { // The order should always be: // [undecided (sortByCreatedAt), scheduled(sortBy publishAt || metadata.intendedPublishAt), asap(sortByCreatedAt)] return [...releases].sort((a, b) { // ...按 releaseType 分段、时间倒序的比较器 }) }入参releases直接来自上层数据流函数保证不污染调用方数组——这正是该规则针对 props/state 场景的落地形态。sortEvents合并多源事件后排序。文档事件流工具先把三路事件拼接成新数组再对新数组就地排序见 events/utils.tsconst eventsWithRemoteEdits [...remoteEdits, ...events, ...expandedEvents].sort( (a, b) { // 发布事件相对其配对的编辑事件优先其余按时间戳倒序 }, )这里排序对象是刚创建出来的拼接结果源数组均不受影响。对照组有意为之的原地排序。结构工具的 pane 元素排序工具对入参数组直接elements.sort(...)见 _sortElements.tselements.sort(_sortByElementPath)这是一个有价值的反例它对HTMLElement[]工作副本做就地排序是刻意的实现选择——数组并非 React state 或 props调用方即数组的所有者。这恰好从反面界定了规则的适用边界该规则约束的是 React 数据流state/props中的数组而非所有sort()调用对完全私有、生命周期可控的工作副本就地排序并非错误只是丧失了「不可变」的显式契约。落地检查清单结合规则文档与上述源码证据在 Sanity 这类 React 代码库中审查排序代码时可按以下清单自检排序的数组是否来自 props、state 或 hook 返回值是则必须使用toSorted()或[...arr].sort()严禁直接arr.sort()。是否支持 ES2023Chrome 110 / Safari 16 / Firefox 115 / Node 20且 TSlib覆盖 ES2023不支持就统一用展开回退保持全库写法一致。reverse()、splice()、arr[i] v等同类就地操作同样适用本规则改用toReversed()、toSpliced()、with()。例外判断若数组是函数内部新创建的纯工作副本如仓库中_sortElements的用法就地排序可接受但建议以局部变量命名体现所有权。该规则的完整上下文见 规则全文同一js-分类下还包含循环合并、正则提升、索引映射等相关的 JavaScript 性能规则可结合 SKILL.md 的索引一并参考。【免费下载链接】sanitySanity Studio – Rapidly configure content workspaces powered by structured content项目地址: https://gitcode.com/GitHub_Trending/sa/sanity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表