ShallowWrapper API 中有兩個易混的 .dive()
和 .shallow()
,調用它們都會返回 ShallowWrapper
,但他們在使用場景和執行過程當中都有所區別。html
.dive([options]) => ShallowWrapper
node
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 HOC
和 Enzyme .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" )。
測試源碼能夠在 這裏找到。
若是宿主對象不是 div
等原生 html DOM 元素,使用哪一個功能同樣。但如 Enzyme(3.11.0)源碼註釋所寫,若是是高階組件,推薦使用 .dive()
,其餘場景,使用 .shallow()
。
Documentation does not make it clear when to use .dive()
vs .shallow()