【发布时间】:2020-10-03 06:05:30
【问题描述】:
我在我的 React 应用程序中使用 react-speech-recognition 将语音转录为文本。 react-speech-recognition 提供了SpeechRecognition 高阶组件,它将browserSupportsSpeechRecognition 等附加属性注入到包装组件中。
我的 App 组件如下所示:
// src/App.js
import React, { useEffect } from 'react';
import SpeechRecognition from 'react-speech-recognition';
const App = ({ transcript, browserSupportsSpeechRecognition }) => {
useEffect(() => {
console.log(`transcript changed: ${transcript}`);
}, [transcript]);
if (! browserSupportsSpeechRecognition) {
return <span className="error">Speech recognition not supported</span>;
}
return <span className="transcript">{transcript}</span>;
};
const options = {
autoStart: false,
continuous: false
};
export default SpeechRecognition(options)(App);
我编写了一些测试来模拟支持语音识别的浏览器和不支持语音识别的浏览器:
// src/App.spec.js
import React from 'react';
import Enzyme, { mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import chai, { expect } from 'chai';
import chaiEnzyme from 'chai-enzyme';
chai.use(chaiEnzyme());
Enzyme.configure({ adapter: new Adapter() });
// Generate a mock SpeechRecognition HOC with the given props
function mockSpeechRecognition(mockProps) {
return function(options) {
return function(WrappedComponent) {
return function(props) {
return (
<WrappedComponent
{...props}
{...mockProps}
recognition={{}}
/>
);
};
};
};
}
describe('App component', () => {
beforeEach(() => jest.resetModules());
it('should show an error when speech recognition is not supported', () => {
jest.mock('react-speech-recognition', () => mockSpeechRecognition({
browserSupportsSpeechRecognition: false
}));
const App = require('./App').default;
const wrapper = mount(<App />);
expect(wrapper).to.contain.exactly(1).descendants('.error');
expect(wrapper.find('.error'))
.to.have.text('Speech recognition not supported');
});
it('should show the transcript when speech recognition is supported', () => {
jest.mock('react-speech-recognition', () => mockSpeechRecognition({
browserSupportsSpeechRecognition: true,
transcript: 'foo'
}));
const App = require('./App').default;
const wrapper = mount(<App />);
expect(wrapper).to.contain.exactly(1).descendants('.transcript');
expect(wrapper.find('.transcript')).to.have.text('foo');
});
});
当我运行这些测试时,我得到一个 "Invalid hook call" error 导致测试失败:
● App component › should show an error when speech recognition is not supported
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/warnings/invalid-hook-call-warning.html for tips about how to debug and fix this problem.
3 |
4 | const App = ({ transcript, browserSupportsSpeechRecognition }) => {
> 5 | useEffect(() => {
| ^
6 | console.log(`transcript changed: ${transcript}`);
7 | }, [transcript]);
8 |
at resolveDispatcher (node_modules/react/cjs/react.development.js:1465:13)
at useEffect (node_modules/react/cjs/react.development.js:1508:20)
at App (src/App.js:5:5)
at renderWithHooks (node_modules/react-dom/cjs/react-dom.development.js:14803:18)
at mountIndeterminateComponent (node_modules/react-dom/cjs/react-dom.development.js:17482:13)
at beginWork (node_modules/react-dom/cjs/react-dom.development.js:18596:16)
at HTMLUnknownElement.callCallback (node_modules/react-dom/cjs/react-dom.development.js:188:14)
at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:193:27)
at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
at Object.invokeGuardedCallbackDev (node_modules/react-dom/cjs/react-dom.development.js:237:16)
at invokeGuardedCallback (node_modules/react-dom/cjs/react-dom.development.js:292:31)
at beginWork$1 (node_modules/react-dom/cjs/react-dom.development.js:23203:7)
at performUnitOfWork (node_modules/react-dom/cjs/react-dom.development.js:22157:12)
at workLoopSync (node_modules/react-dom/cjs/react-dom.development.js:22130:22)
at performSyncWorkOnRoot (node_modules/react-dom/cjs/react-dom.development.js:21756:9)
at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:21188:7)
at updateContainer (node_modules/react-dom/cjs/react-dom.development.js:24373:3)
at node_modules/react-dom/cjs/react-dom.development.js:24758:7
at unbatchedUpdates (node_modules/react-dom/cjs/react-dom.development.js:21903:12)
at legacyRenderSubtreeIntoContainer (node_modules/react-dom/cjs/react-dom.development.js:24757:5)
at Object.render (node_modules/react-dom/cjs/react-dom.development.js:24840:10)
at fn (node_modules/enzyme-adapter-react-16/src/ReactSixteenAdapter.js:437:26)
at node_modules/enzyme-adapter-react-16/src/ReactSixteenAdapter.js:354:37
at batchedUpdates$1 (node_modules/react-dom/cjs/react-dom.development.js:21856:12)
at Object.act (node_modules/react-dom/cjs/react-dom-test-utils.development.js:929:14)
at wrapAct (node_modules/enzyme-adapter-react-16/src/ReactSixteenAdapter.js:354:13)
at Object.render (node_modules/enzyme-adapter-react-16/src/ReactSixteenAdapter.js:423:16)
at new ReactWrapper (node_modules/enzyme/src/ReactWrapper.js:115:16)
at mount (node_modules/enzyme/src/mount.js:10:10)
at Object.<anonymous> (src/App.spec.js:38:25)
● App component › should show the transcript when speech recognition is supported
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/warnings/invalid-hook-call-warning.html for tips about how to debug and fix this problem.
3 |
4 | const App = ({ transcript, browserSupportsSpeechRecognition }) => {
> 5 | useEffect(() => {
| ^
6 | console.log(`transcript changed: ${transcript}`);
7 | }, [transcript]);
8 |
at resolveDispatcher (node_modules/react/cjs/react.development.js:1465:13)
at useEffect (node_modules/react/cjs/react.development.js:1508:20)
at App (src/App.js:5:5)
at renderWithHooks (node_modules/react-dom/cjs/react-dom.development.js:14803:18)
at mountIndeterminateComponent (node_modules/react-dom/cjs/react-dom.development.js:17482:13)
at beginWork (node_modules/react-dom/cjs/react-dom.development.js:18596:16)
at HTMLUnknownElement.callCallback (node_modules/react-dom/cjs/react-dom.development.js:188:14)
at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:193:27)
at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
at Object.invokeGuardedCallbackDev (node_modules/react-dom/cjs/react-dom.development.js:237:16)
at invokeGuardedCallback (node_modules/react-dom/cjs/react-dom.development.js:292:31)
at beginWork$1 (node_modules/react-dom/cjs/react-dom.development.js:23203:7)
at performUnitOfWork (node_modules/react-dom/cjs/react-dom.development.js:22157:12)
at workLoopSync (node_modules/react-dom/cjs/react-dom.development.js:22130:22)
at performSyncWorkOnRoot (node_modules/react-dom/cjs/react-dom.development.js:21756:9)
at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:21188:7)
at updateContainer (node_modules/react-dom/cjs/react-dom.development.js:24373:3)
at node_modules/react-dom/cjs/react-dom.development.js:24758:7
at unbatchedUpdates (node_modules/react-dom/cjs/react-dom.development.js:21903:12)
at legacyRenderSubtreeIntoContainer (node_modules/react-dom/cjs/react-dom.development.js:24757:5)
at Object.render (node_modules/react-dom/cjs/react-dom.development.js:24840:10)
at fn (node_modules/enzyme-adapter-react-16/src/ReactSixteenAdapter.js:437:26)
at node_modules/enzyme-adapter-react-16/src/ReactSixteenAdapter.js:354:37
at batchedUpdates$1 (node_modules/react-dom/cjs/react-dom.development.js:21856:12)
at Object.act (node_modules/react-dom/cjs/react-dom-test-utils.development.js:929:14)
at wrapAct (node_modules/enzyme-adapter-react-16/src/ReactSixteenAdapter.js:354:13)
at Object.render (node_modules/enzyme-adapter-react-16/src/ReactSixteenAdapter.js:423:16)
at new ReactWrapper (node_modules/enzyme/src/ReactWrapper.js:115:16)
at mount (node_modules/enzyme/src/mount.js:10:10)
at Object.<anonymous> (src/App.spec.js:52:25)
但是,当我运行开发服务器并在浏览器中查看页面时,没有这样的错误,我可以看到 useEffect 挂钩将消息记录到控制台。创建生产版本时也没有错误。我认为问题在于我如何嘲笑SpeechRecognition HOC。如果我删除 useEffect 钩子,则测试通过。
这是一个从 create-react-app 开始的全新项目。我只有一份 react 和 react-dom 副本,并且版本匹配:
$ npm ls react react-dom
react-speech-recognition-invalid-hook-call@0.1.0 /Users/NMD/max_programming_projects/react-speech-recognition-invalid-hook-call
├── react@16.13.1
└── react-dom@16.13.1
如何在我的测试中修复这个错误?
【问题讨论】:
-
最后,我从 react-speech-recognition 切换到了react-speech-kit,因为它的界面更好,不需要我做任何复杂的模拟。
标签: reactjs jestjs enzyme higher-order-components