
enzyme ReactWrapper.filterWhere 方法详解基于谓词函数的节点过滤【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme.filterWhere(predicate)是 enzyme 中用于按自定义条件过滤 wrapper 内节点的核心方法它允许开发者绕过选择器语法的限制通过一个返回布尔值的谓词函数predicate对当前 wrapper 中的每个节点进行精细筛选并返回一个只包含符合条件节点的新 wrapper。在 React 组件测试中它常用于只保留复合组件节点剔除宿主 DOM 节点等选择器难以直接表达的场景也是 enzyme 内部实现filter、not、hostNodes等方法的底层基础设施。读完本文你将掌握filterWhere的完整签名、谓词函数的调用约定、底层实现原理以及在实际测试中组合使用它的实战技巧。方法签名与返回值在 enzyme 中ReactWrapper由mount()产生和ShallowWrapper由shallow()产生都提供了同名方法filterWhere两者的语义与用法完全一致仅返回的 wrapper 类型不同// ReactWrapper 版本 .filterWhere(predicate) ReactWrapper // ShallowWrapper 版本 .filterWhere(predicate) ShallowWrapper参数predicateReactWrapper Boolean或ShallowWrapper Boolean——一个接收包装节点wrapped node的谓词函数返回true表示保留该节点。返回值一个新的 wrapper只包含当前 wrapper 中通过谓词函数测试的节点。原 wrapper 不会被修改这与 enzyme 大部分查询方法返回新 wrapper、保持不可变的设计一致。官方文档对它的定义是Returns a new wrapper with only the nodes of the current wrapper that, when passed into the provided predicate function, return true.返回一个新的 wrapper其中只包含当前 wrapper 中传入提供的谓词函数后返回true的那些节点。这一点可以从文档原文 ReactWrapper/filterWhere.md 与 ShallowWrapper/filterWhere.md 两处得到印证。参数详解谓词函数收到的是什么filterWhere的参数只有一个谓词函数。它最关键、也最容易踩坑的约定是——谓词函数收到的参数不是裸的 React 节点而是一个包装后的 wrapper 实例即ReactWrapper或ShallowWrapper。这意味着你可以在谓词内部直接调用该 wrapper 的全部实例方法如.type()、.props()、.hasClass()、.name()等。共享测试套件中的用例明确验证了这一约定见 filterWhere.jsxconst stub sinon.stub(); stub.returns(true); const spy sinon.spy(stub); wrapper.find(.foo).filterWhere(spy); expect(spy).to.have.property(callCount, 3); // 每个节点调用一次 expect(spy.args[0][0]).to.be.instanceOf(Wrapper); // 参数是包装后的节点 expect(spy.args[0][0].hasClass(bar)).to.equal(true); // 可直接调用 wrapper 方法从测试中可以提炼出三个关键事实谓词函数对当前 wrapper 的每个节点各调用一次callCount等于节点数传入参数始终是该节点对应的 wrapper 实例instanceOf(Wrapper)谓词返回false的节点会被剔除返回true的节点被保留且保留节点的顺序与原有顺序一致。官方示例与实战扩展官方文档给出的示例ReactWrapper/filterWhere.md利用复合组件节点的type()不是字符串这一特性从一批.foo节点中过滤出所有复合组件即非 DOM 宿主节点const wrapper mount(MyComponent /); const complexComponents wrapper.find(.foo).filterWhere((n) typeof n.type() ! string); expect(complexComponents).to.have.lengthOf(4);在 enzyme 中n.type()对宿主 DOM 元素如div返回字符串标签名如div而对复合组件返回组件构造函数或函数本身非字符串因此上面的断言可以精确筛选出是组件而非原生元素的节点。结合谓词函数能调用 wrapper 方法的能力还可以写出更多实用变体// 只保留带有指定 class 的节点 const active wrapper.find(.item).filterWhere((n) n.hasClass(active)); // 只保留传入了指定 props 的节点 const disabled wrapper.find(button).filterWhere((n) n.props().disabled true); // 只保留 name 以特定前缀开头的复合组件 const widgets wrapper.findWhere((n) n.type() ! string) .filterWhere((n) n.name().startsWith(Widget));注意最后一种组合中findWhere是在整棵渲染树中递归查找而filterWhere只对当前 wrapper 已有的节点集合做过滤两者作用范围不同不能混为一谈。底层实现原理三个方法共享的过滤核心阅读源码可以发现filterWhere、filter、not三个方法共享同一个私有核心函数filterWhereUnwrapped见 ReactWrapper.js 与 ShallowWrapper.jsfunction filterWhereUnwrapped(wrapper, predicate) { return wrapper.wrap(wrapper.getNodesInternal().filter(predicate).filter(Boolean)); }这个核心函数只做了三件事wrapper.getNodesInternal()取出当前 wrapper 内部的节点数组.filter(predicate)对节点数组应用谓词过滤末尾的.filter(Boolean)用于剔除过滤后可能残留的空值如null/undefined节点保证返回结果干净wrapper.wrap(...)将过滤后的节点数组重新包装成新的 wrapper 实例返回。而公共 APIfilterWhere本身只是一层薄封装关键区别在于——它把原始节点再次包装后传给用户提供的谓词// ReactWrapper.js L593-595 filterWhere(predicate) { return filterWhereUnwrapped(this, (n) predicate(this.wrap(n))); } // ShallowWrapper.js L1054-1056 filterWhere(predicate) { return filterWhereUnwrapped(this, (n) predicate(this.wrap(n))); }注意其中的this.wrap(n)谓词接收到的不是内部裸节点n而是被wrap成 wrapper 的节点。这正是上一节谓词收到的参数是 wrapper 实例这一约定的源码出处也解释了为什么可以在谓词内部调用.type()、.hasClass()等方法。与 filter、not、hostNodes 的关联filterWhere在整个 enzyme 的方法体系中处于基础地位其他几个常用方法都建立在它之上相关源码见 ReactWrapper.js 与 ShallowWrapper.jsfilter(selector)选择器版本的 filterWherefilter(selector) { const predicate buildPredicate(selector); return filterWhereUnwrapped(this, predicate); }filter通过buildPredicate(selector)把 CSS 风格的选择器编译成一个谓词函数再交给同一个filterWhereUnwrapped执行。两者的关系可以理解为filter是选择器即谓词的特例filterWhere是任意自定义逻辑的通用形式。官方文档 ReactWrapper/filter.md 也把两者互为关联方法列出。not(selector)filterWhere 的取反not(selector) { const predicate buildPredicate(selector); return filterWhereUnwrapped(this, (n) !predicate(n)); }not在内部把选择器编译出的谓词取反后再次走filterWhereUnwrapped等价于filterWhere((n) !n.matches(selector))。hostNodes()官方内置的 filterWhere 应用hostNodes是filterWhere最直接的官方用例它只保留真正渲染成 DOM 的宿主节点见 ReactWrapper.js 与 ShallowWrapper.jshostNodes() { return this.filterWhere((n) typeof n.type() string); }它用typeof n.type() string作为谓词与官方示例中的! string恰好互为取反——一个筛出 DOM 宿主节点一个筛出复合组件节点。这个对照关系是理解type()返回值约定宿主元素返回字符串标签名的最佳切入点。与 everyWhere / someWhere / findWhere 的区分在 enzyme 的文档体系中与filterWhere同族的方法还有.everyWhere(predicate)返回布尔值判断是否所有节点都满足谓词等价于filterWhere后长度不变但语义更直接.someWhere(predicate)返回布尔值判断是否存在至少一个节点满足谓词.findWhere(predicate)在整棵渲染树中递归查找满足谓词的节点返回的新 wrapper 可能包含多级节点.filterWhere(predicate)仅对当前 wrapper 的直接节点集合做一层过滤不做递归。它们的谓词约定完全一致都接收包装后的节点差别仅在于遍历范围一层 vs 整棵树与聚合语义保留 vs 判定。filterWhere与findWhere在 ReactWrapper 和 ShallowWrapper 中的实现也共用同一套Unwrapped辅助函数体系便于保持一致的行为。使用建议与注意事项结合源码与测试用例归纳几点工程实践建议谓词必须是纯函数且无副作用谓词会对每个节点恰好调用一次且结果仅用于决定节点去留在其中调用setState、setProps等变更方法会造成难以预期的行为。基于选择器 vs 基于谓词的选择能明确表达的需求优先用.filter(selector)更简洁、可读性更好当条件涉及 props 之间的组合逻辑、类型判断、多个 wrapper 方法联动时再用filterWhere。链式调用是惯用法filterWhere返回新 wrapper因此可以继续链式调用.map()、.forEach()、.some()等其余实例方法官方示例中的wrapper.find(.foo).filterWhere(...)就是典型的先定位再过滤组合。注意与mount/shallow的配套ReactWrapper版本配合mount()使用需在 jsdom 等含 DOM 的环境下运行详见 安装与运行指南ShallowWrapper版本配合shallow()使用两者的谓词参数分别是ReactWrapper与ShallowWrapper实例。空结果的处理若所有节点都被过滤掉返回的是长度为 0 的新 wrapper内部.filter(Boolean)会清除空值节点可配合.exists()、.length属性做断言不会抛出异常。小结.filterWhere(predicate)是 enzyme 节点查询体系中以函数代替选择器的通用入口它以接收包装节点、返回布尔值的谓词为核心约定通过共享的filterWhereUnwrapped核心逻辑实现不可变的过滤语义并向下支撑着filter、not、hostNodes等高频方法。理解它的参数约定与实现链路能够让你在处理复杂节点筛选需求时写出更精准、更易维护的组件测试。完整的方法列表可查阅 ReactWrapper API 索引 与 ShallowWrapper API 索引 同目录下的对应文档。【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考