如何测试方法 shouldComponentUpdate?

Posted

技术标签:

【中文标题】如何测试方法 shouldComponentUpdate?【英文标题】:How do I test the method shouldComponentUpdate? 【发布时间】:2019-12-29 17:13:12 【问题描述】:

我想测试该方法,shouldComponentUpdate,但我编写的测试没有捕捉到副作用。我该如何测试?

我已经尝试过这里提出的解决方案,但存在一些问题,我将在代码中显示:How to unit test React Component shouldComponentUpdate method

我尝试过使用shallow 而不是mount,但是当我这样做时,我的测试在这一点上失败了:

    expect(shouldComponentUpdate).to.have.property("callCount", 1);
    // AssertionError: expected [Function: proxy] to have a property 'callCount' of 1, but got 0

这是我的组件的shouldComponentUpdate 函数:

    shouldComponentUpdate(nextProps, nextState) 
        if (this.props.storageValue !== nextProps.storageValue) 
            localStorage.setItem(constantFile.storageKey, nextProps.storageValue);
            localStorage.setItem(constantFile.timestampKey, now());
            this.setState(
                newStorage: nextProps.storageValue
            );
            return true;
        

        ...

        return false;
    

这是我的测试:

    it("Test shouldComponentUpdate", () => 
        /* eslint-disable one-var */
        const wrapper = mount(
            <Provider store=store>
                <MyComponent />
            </Provider>),
            shouldComponentUpdate = sinon.spy(CapitalHomeAccessPointContainer.prototype, "shouldComponentUpdate");
        wrapper.setProps(
            storageValue: true
        );
        expect(shouldComponentUpdate).to.have.property("callCount", 1);   
        expect(shouldComponentUpdate.returned(true)).to.be.equal(true);
  expect(localStorage.getItem(constantFile.timestampKey)).to.equal(someExpectedTimestamp);
    );
expect(shouldComponentUpdate).to.have.property("callCount", 1);

失败

AssertionError: expected false to equal true

为什么会这样?我认为shouldComponentUpdate 应该返回true?后来,我希望测试状态和本地存储的副作用,但我不明白这个错误。

【问题讨论】:

需要进行状态更改才能触发shouldComponentUpdate。您还应该测试函数的结果,而不是如果它被调用。 我认为文档说,“使用 shouldComponentUpdate() 让 React 知道组件的输出是否不受当前状态或道具变化的影响。默认行为是在每个状态上重新渲染更改,并且在绝大多数情况下,您应该依赖默认行为”。 https://reactjs.org/docs/react-component.html#shouldcomponentupdate 所以我认为如果在 shouldComponentUpdate 中指定,道具的变化也应该触发 true? 您还提到要测试该功能的副作用。我也尝试这样做。 ```expect(wrapper.find("MyComponent").state("newStorage")).to.be.true; ``` 导致这个错误:``` AssertionError: expected false to be true ``` 【参考方案1】:

您正在处理的 shouldComponentUpdate 生命周期是错误的。你不在其中setState,而是只返回一个boolean(true 或false)让React 知道何时重新渲染DOM。这允许您阻止来自外部道具的更新。例如:

shouldComponentUpdate(nextProps, nextState) 
 return this.state.newStorage !== nextState.newStorage

上述状态:如果当前 newStorage 状态与下一个 newStorage 状态不匹配,则重新渲染组件。 以上内容会阻止 prop 更改重新渲染组件。如果您更改了 storage 道具,它不会更新此组件,因为状态没有更改。您想使用 shouldComponentUpdate 来防止不必要的双重重新渲染和/或防止父组件的重新渲染不必要地重新渲染子组件。在这种情况下,您不需要使用此生命周期方法。

相反,您应该使用 static getDerivedStateFromProps 属性来更新渲染前的状态,或者使用 componentDidUpdate 来更新渲染后的状态。

在您的情况下,由于您只想在this.props.storageValue 更改时更新状态,因此您应该使用componentDidUpdate

componentDidUpdate(prevProps) 
   if (this.props.storageValue !== prevProps.storageValue) 
     localStorage.setItem(constantFile.storageKey, nextProps.storageValue);
     localStorage.setItem(constantFile.timestampKey, now());
     this.setState( newStorage: nextProps.storageValue );
   

上面检查当前的 props 是否与之前的 props 不匹配,然后更新状态以反映变化。

您将无法使用static getDerivedStateFromProps,因为没有新旧道具之间的比较,而只是与传入道具和当前状态的比较。也就是说,如果您将某些内容存储在与存储道具直接相关的状态中,则可以使用它。一个示例可能是,如果您将密钥从道具存储到状态中。如果 props 键要更改并且它与状态中的当前键不匹配,那么它将更新状态以反映键更改。在这种情况下,您将返回 object 以更新状态或返回 null 以不更新状态。

例如:

static getDerivedStateFromProps(props, state) 
  return props.storage.key !== state.storageKey 
  ?  storageKey: props.storage.key 
  : null


在测试方面,您需要手动更新 props 以影响状态更改。通过针对状态更改进行测试,您将间接针对 componentDidUpdate 进行测试。

例如(你必须mock localStorage,否则状态不会更新——我还建议exporting class 并导入它,而不是使用 Redux 连接组件):

import  MyComponent  from "../MyComponent.js";

const initialProps =  storageValue: "" ;

describe("Test Component", () => 
  let wrapper;
  beforeEach(() => 
    wrapper = mount(<MyComponent  ...initialProps  />);
  )

  it("updates 'newStorage' state when 'storageValue' props have changed, () => 
    wrapper.setProps( storageValue: "test" );  
    expect(wrapper.state('NewStorage')).toEqual(...); // this should update NewStorage state

    wrapper.setProps( someOtherProp: "hello" );
    expect(wrapper.state('NewStorage')).toEqual(...); // this should not update NewStorage state 
  );
);

【讨论】:

以上是关于如何测试方法 shouldComponentUpdate?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 React 保留 componentWillReceiveProps 和 shouldComponentUpdate 方法?

[react] shouldComponentUpdate方法是做什么的

React Hooks - 我如何实现 shouldComponentUpdate?

如何将 shouldComponentUpdate 与 React Hooks 一起使用?

如何将 shouldComponentUpdate 与 React Hooks 一起使用?

使用shouldComponentUpdate进行性能优化