2016-12-22 58 views
4

在我的測試中,我想阻止我的主線程,直到我的某個組件完成其整個生命週期方法(通過componentDidUpdate(),完成後我觸發導致其添加子組件的事件自己。我該怎麼做?等待React組件完成更新

事情是這樣的:

describe('my <Component />',() => { 
    it('should do what I want when its button is clicked twice',() => { 
    const wrapper = mount(<Component />); 
    const button = wrapper.find('button'); 

    // This next line has some side effects that cause <Component /> to add 
    // some children components to itself... 
    button.simulate('click', mockEvent); 

    // ... so I want to wait for those children to completely go through 
    // their lifecycle methods ... 
    wrapper.instance().askReactToBlockUntilTheComponentIsFinishedUpdating(); 

    // ... so that I can be sure React is in the state I want it to be in 
    // when I further manipulate the <Component /> 
    button.simulate('click', mockEvent); 

    expect(whatIWant()).to.be.true; 
    }); 
}); 

(我想這樣做,因爲,現在,我得到這樣的警告:

Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. 

我相信我得到它,因爲我的測試引起我的組件比React的內部多線程魔術更快地改變其內部狀態,因此,當我第二次運行button.simulate('click')時,React已經實例化了新的子組件,但還沒有完成它們的安裝。 React完成更新荷蘭國際集團我的組件及其子是解決這個問題的最好辦法)

+1

是什麼單擊處理呢?所有的狀態改變應該是同步的,除非你特別有一些定時器/異步的東西正在進行。 – Jacob

+1

DOM更新也是同步的,除非你做了一些奇怪的事情或者使用了React的異國情調。如果你的'setState'警告是因爲你的組件本身在mount之前做了'setState',我不會感到驚訝。 – Jacob

+1

@Jacob我正在使用第三方庫[React Widgets](https://jquense.github.io/react-widgets/docs/)。我的''有一個React Widgets'DateTimePicker'作爲它的一個孩子,並且警告似乎是從'DateTimePicker'內發出的。所以不幸的是,我確切地知道我的事件會導致什麼變化,我很容易無法嘗試查看組件,以確保它不會行爲不端。 – Kevin

回答

1

嘗試在setImmediate()塊包裹你的expect()塊:

describe('my <Component />',() => { 
    it('should do what I want when its button is clicked twice', (done) => { 
    const wrapper = mount(<Component />); 
    const button = wrapper.find('button'); 

    button.simulate('click', mockEvent); 
    button.simulate('click', mockEvent); 

    setImmediate(() => { 
     expect(whatIWant()).to.be.true; 
     done(); 
    }); 
    }); 
}); 

這裏的是怎麼回事:爲了處理異步,節點和大多數瀏覽器幕後有一個事件隊列。無論何時,如Promise或IO事件需要異步運行,JS環境都會將其添加到隊列末尾。然後,只要同步代碼運行完畢,環境就會檢查隊列,選擇隊列前端的任何內容並運行該隊列。

setImmediate()向隊列的後面添加一個函數。一旦當前隊列中的所有內容都運行完畢,傳遞給setImmediate()的函數中的任何內容都將運行。因此,無論React如何異步執行,將expect()s包裝在setImmediate()之內都會導致您的測試等待,直到React完成其在後臺執行的任何異步工作。

這裏有更多有關setImmediate()一個很大的問題:setImmediate vs. nextTick

下面是在節點setImmediate()的文檔:https://nodejs.org/api/timers.html#timers_setimmediate_callback_args

+0

我在我的一些測試中試過這個'setImmediate()'技巧,但並沒有在我的問題中描述的具體情況。所以有龍,這種解決方案可能無法正常工作。 – Kevin

+0

當'setImmediate()'中的斷言('expect')失敗時,測試終止並顯示錯誤,而不是將其標記爲「失敗」並繼續。任何工作? – vleong

+0

@VLeong您是否使用'it'塊中的'done'函數將測試寫入異步測試?請參閱https://mochajs.org/#asynchronous-code – Kevin