Enzyme dive() 和 shallow() 的区别

ShallowWrapper API 中有两个易混的 .dive().shallow(),调用它们都会返回 ShallowWrapper,但他们在使用场景和执行过程当中都有所区别。html

API 定义

  • .dive([options]) => ShallowWrappernode

    Shallow render the one non-DOM child of the current wrapper, and return a wrapper around the result.react

    non-DOM child 指的是 非 dom 的 reactElement(不能是 div span 等)
  • .shallow([options]) => ShallowWrapper

    Shallow renders the current node and returns a shallow wrapper around it.git

源码解析

  • .dive() 源码github

    dive(options = {}) {
      const adapter = getAdapter(this[OPTIONS]);
      const name = 'dive';
      return this.single(name, (n) => {
        if (n && n.nodeType === 'host') {
          throw new TypeError(`ShallowWrapper::${name}() can not be called on Host Components`);
        }
        const el = getAdapter(this[OPTIONS]).nodeToElement(n);
        if (!isCustomComponentElement(el, adapter)) {
          throw new TypeError(`ShallowWrapper::${name}() can only be called on components`);
        }
        const childOptions = makeInheritedChildOptions(this, options);
        return this.wrap(el, null, childOptions);
      });
    }
  • .shallow() 源码web

    shallow(options = {}) {
      return this.single('shallow', (n) => {
        const childOptions = makeInheritedChildOptions(this, options);
        return this.wrap(getAdapter(this[OPTIONS]).nodeToElement(n), null, childOptions);
      });
    }

咱们能够将 .dive() 源码稍做变化:app

dive(options = {}) {
  return this.single('dive', (n) => {
    // 错误判断-start
    if (n && n.nodeType === 'host') {
      throw new TypeError(`ShallowWrapper::${name}() can not be called on Host Components`);
    }
    const el = getAdapter(this[OPTIONS]).nodeToElement(n);
    const adapter = getAdapter(this[OPTIONS]);
    if (!isCustomComponentElement(el, adapter)) {
      throw new TypeError(`ShallowWrapper::${name}() can only be called on components`);
    }
    // 错误判断-end
    const childOptions = makeInheritedChildOptions(this, options);
    return this.wrap(getAdapter(this[OPTIONS]).nodeToElement(n), null, childOptions);
  });
}

经过对比变体后的 .dive().shallow() 对比,若是去掉错误判断部分,咱们能够看到,.dive() 的逻辑和 .shallow() 同样。一般,.dive() 配合高阶组件一块儿使用,如在 Enzyme 仓库这个 issue 中 Documentation does not make it clear when to use .dive() vs .shallow()@Jordan Harband 提到dom

.dive() is sugar for "throw if there's more than one child, throw if that child isn't a custom component, call .shallow on it". It was a common enough pattern that it warranted first-class inclusion.
In particular, the mantra i often use is "when shallow rendering, one .dive() per HOC"

若是仅从功能来看,能用 .dive() 的场景一样可使用 .shallow() ,且结果同样。测试

.dive() 内部作了更加严格的类型判断(不能是 Host Components,且必须是自定义的 components。对 web 而言,div 等原生 html DOM 元素,以及 null 或者 react 内建组件 <Fragment> 都不行),而且,在高阶组件中使用,语义也更加友好:dive 到高阶组件包裹的自定义组件中(后文有例子)。this

With respect to renderers there are two types of react components:
Host Components: Host components are platform-specific components, such as <div> or a <View> and they run platform-specific code such as mounting, updates, and unmounting of DOM/Native view. They typically begin with lower-case in the case of ReactDom.
Composite Components: Composite components are user-defined components such as <MyButton> or <Content> and they behave the same way with all renderers. React will calls methods, such as render() and componentDidMount(), on the user-supplied composite components.

使用场景

宿主对象类型

.dive() 只能用于非 DOM 的 wrapper,而 .shallow() 没有此限制,若是成功输出 ShallowWrapper,结果都同样。看下面代码:

test('试验', () => {
  class Bar extends React.Component {
    render() {
      return (
        <div>
          <div className="in-bar" />
        </div>
      );
    }
  }

  class Foo extends React.Component {
    render() {
      return (
        <div>
          <Bar />
        </div>
      );
    }
  }

  const wrapper = shallow(<Foo />);

  // 由于浅渲染,Bar 在当前 wrapper 中只会渲染成 <Bar />,因此找不到其包含的 '.in-bar'
  expect(wrapper.find('.in-bar').length).toBe(0);

  // 这里找到的 Bar 是 <Bar />
  expect(wrapper.find('Bar').length).toBe(1);

  // 对 '<Bar />' 使用 dive() 后,将会渲染它,而后就能够找到 '.in-bar'了
  expect(wrapper.find('Bar').dive().find('.in-bar').length).toBe(1);
  // 这里可使用 shallow() 效果相同
  expect(wrapper.find('Bar').shallow().find('.in-bar').length).toBe(1);

  // dive() 只能应用于 非 DOM
  console.log(wrapper.find(Bar).dive().find('.in-bar').dive().debug()); // 报错,不能对 dom 使用
  console.log(wrapper.find(Bar).dive().find('.in-bar').shallow().debug()); // 正常输出 html
});
.dive() only works on a wrapper around a single element from a custom component. If you want to shallow on an HTML element, or multiple, you'd need .shallow(). I think the use cases are rare, but they exist. by @Jordan Harband

配合高阶组件使用

const withHOC = Component => props => (
  <Component testHOCProp="come from HOC" {...props} />
);
class Bar extends React.Component {
  render() {
    return (
      <div>
        <div className="in-bar" />
      </div>
    );
  }
}
class EnzymeDive extends React.Component {
  shouldComponentUpdate() {
    console.log(1111);
  }
  render() {
    return (
      <div>
        Enzyme Dive
        <Bar />
      </div>
    );
  }
}

test('Enzyme .dive() with HOC', () => {
  const wrapper = shallow(<EnzymeDive />);
  wrapper.dive().setProps();

  console.log('====wrapper.debug()====', wrapper.debug());
  console.log('====wrapper.dive().debug()====', wrapper.dive().debug());
});

test('Enzyme .shallow() with HOC', () => {
  const wrapper = shallow(<EnzymeDive />);
  wrapper.shallow().setProps();

  console.log('====wrapper.debug()====', wrapper.debug());
  console.log('====wrapper.dive().debug()====', wrapper.shallow().debug());
});

两个测试用例 Enzyme .dive() with HOCEnzyme .shallow() with HOC 输出的结果都同样,且对于 EnzymeDive 组件而言,测试覆盖率都达到了 100%。其中

  • wrapper.debug() 的输出结果为:
<EnzymeDive testHOCProp="come from HOC" />
  • wrapper.dive().debug()wrapper.shallow().debug() 的输出结果为:
<div>
  Enzyme Dive
  <Bar />
</div>

能够看到 .dive().shallow() 都 "unwrap" 了高阶组件("unwrap" here just means "shallow-render one level deeper" )。

image.png

测试源码能够在 这里找到。

结论

若是宿主对象不是 div 等原生 html DOM 元素,使用哪一个功能同样。但如 Enzyme(3.11.0)源码注释所写,若是是高阶组件,推荐使用 .dive(),其余场景,使用 .shallow()

参考资料

Documentation does not make it clear when to use .dive() vs .shallow()

相关文章
相关标签/搜索