【发布时间】:2025-11-25 22:30:01
【问题描述】:
这是我第一次在 react.-native 中使用 Redux Hooks,但是我在测试中遇到了问题,因为当我运行测试时,我收到了这个警告:
警告:React 检测到 Hooks 调用的顺序发生了变化 保修主页按钮。如果不修复,这将导致错误和错误。 欲了解更多信息,请阅读 Hooks 规则:
Previous render Next render ------------------------------------------------------ 1. useState useContext ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
我不确定问题是我的代码还是我测试的方式,应用程序运行良好。如果我独立运行每个测试,则测试通过而没有警告,如果我运行所有三个测试,最后一个测试会抛出警告和错误。
如果你们能指出我犯错的正确方向,我将不胜感激。
WarrantiesHomeButton.js
import React, { useState, useEffect, useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { WarrantiesHomeMenu } from '../WarrantiesHomeMenu';
import { userService } from '../services';
import { HomeButton } from '../components';
import { screenNames } from '../constants';
import { userActions } from '../store';
import PropTypes from 'prop-types';
const WarrantiesHomeButton = ({ navigation }) => {
const [showWarrantiesMenu, setShowWarrantiesMenu] = useState(false);
const { warrantiesLoginFlow, user } = useSelector(
(state) => state.userReducer,
);
const dispatch = useDispatch();
const handlePressWarranties = () => {
if (!userService.isAuthenticated()) {
dispatch(userActions.warrantiesLoginStart());
navigation.navigate(screenNames.SIGN_UP);
} else {
setShowWarrantiesMenu(true);
}
};
useEffect(() => {
validateWarrantiesFlow();
});
const validateWarrantiesFlow = useCallback(() => {
if (warrantiesLoginFlow && user.id) {
setShowWarrantiesMenu(true);
dispatch(userActions.warrantiesLoginStop());
}
}, [warrantiesLoginFlow, user.id, dispatch]);
const handleModalPress = () => {
setShowWarrantiesMenu(false);
navigation.navigate(screenNames.SERVICE_CENTERS);
};
const closeWarrantiesHomeMenu = () => {
setShowWarrantiesMenu(false);
};
return (
<>
<HomeButton
testID="HomeButton"
text="Revisiones garantía"
icon="toolBox"
onPress={handlePressWarranties}
/>
<WarrantiesHomeMenu
visible={showWarrantiesMenu}
onPress={handleModalPress}
navigation={navigation}
closeModal={closeWarrantiesHomeMenu}
/>
</>
);
};
WarrantiesHomeButton.propTypes = {
navigation: PropTypes.objectOf(PropTypes.any),
};
export { WarrantiesHomeButton };
WarrantiesHomeButton.test.js
import * as React from 'react';
import { Provider } from 'react-redux';
import {
render as rtlRender,
fireEvent,
wait,
act,
} from '@testing-library/react-native';
import { userService } from '../services';
import { screenNames } from '../constants';
import { WarrantiesHomeButton } from './WarrantiesHomeButton';
import { store } from '@auteco/store';
jest.mock('@auteco/components', () => require('@auteco/test').mockComponents());
jest.mock('../WarrantiesHomeMenu', () => {
const { mockComponent } = require('@auteco/test');
return {
WarrantiesHomeMenu: mockComponent('WarrantiesHomeMenu'),
};
});
describe('<WarantiesHomeButton/>', () => {
let mockProps;
const setState = jest.fn();
beforeAll(() => {
mockProps = {
navigation: {
navigate: jest.fn(),
dangerouslyGetParent: jest.fn(() => ({ state: null })),
},
};
jest.spyOn(userService, 'isAuthenticated').mockReturnValue(true);
});
function render(ui) {
return rtlRender(<Provider store={store}>{ui}</Provider>);
}
it('Should Render correctly', () => {
const { baseElement } = render(<WarrantiesHomeButton {...mockProps} />);
expect(baseElement).toMatchSnapshot();
});
describe('And user is authenticated', () => {
describe('And button is pressed', () => {
it('Should show WarrantiesMenu by setting setShowWarrantiesMenu state to true', async () => {
jest.spyOn(React, 'useState').mockReturnValue([false, setState]);
const { getByTestId } = render(<WarrantiesHomeButton {...mockProps} />);
const button = getByTestId('HomeButton');
button.props.onPress();
await wait();
expect(setState).toBeCalledWith(true);
});
});
});
describe('And user is not authenticated', () => {
describe('And button is pressed', () => {
it('Should navigate to signUpScreen', async () => {
jest.spyOn(userService, 'isAuthenticated').mockReturnValue(false);
jest.spyOn(React, 'useState').mockReturnValue([false, setState]);
const { getByTestId } = render(<WarrantiesHomeButton {...mockProps} />);
const button = getByTestId('HomeButton');
act(() => {
button.props.onPress();
});
expect(mockProps.navigation.navigate).toBeCalledWith(
screenNames.SIGN_UP,
);
});
});
});
});
控制台出错
console.error node_modules/react-native/Libraries/YellowBox/YellowBox.js:63
Warning: React has detected a change in the order of Hooks called by WarrantiesHomeButton. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks
Previous render Next render
------------------------------------------------------
1. useState useContext
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
in WarrantiesHomeButton
in Provider
in View (created by View)
in View (created by AppContainer)
in View (created by View)
in View (created by AppContainer)
in AppContainer (at src/index.js:26)
console.error node_modules/react-native/Libraries/YellowBox/YellowBox.js:63
The above error occurred in the <WarrantiesHomeButton> component:
in WarrantiesHomeButton
in Provider
in View (created by View)
in View (created by AppContainer)
in View (created by View)
in View (created by AppContainer)
in AppContainer (at src/index.js:26)
Consider adding an error boundary to your tree to customize error handling behavior.
FAIL src/components/home/WarrantiesHomeButton/WarrantiesHomeButton.test.js
<WarantiesHomeButton/>
✓ Should Render correctly (55ms)
And user is authenticated
And button is pressed
✓ Should show WarrantiesMenu by setting setShowWarrantiesMenu state to true (12ms)
And user is not authenticated
And button is pressed
✕ Should navigate to signUpScreen (77ms)
● <WarantiesHomeButton/> › And user is not authenticated › And button is pressed › Should navigate to signUpScreen
TypeError: Cannot read property 'length' of undefined
11 | const [showWarrantiesMenu, setShowWarrantiesMenu] = useState(false);
12 |
> 13 | const { warrantiesLoginFlow, user } = useSelector(
| ^
14 | (state) => state.userReducer,
15 | );
16 | const dispatch = useDispatch();
at areHookInputsEqual (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5703:38)
at updateMemo (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6336:11)
at Object.useMemo (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6703:16)
at useMemo (node_modules/react/cjs/react.development.js:1592:21)
at useSelectorWithStoreAndSubscription (node_modules/react-redux/lib/hooks/useSelector.js:31:41)
at useSelector (node_modules/react-redux/lib/hooks/useSelector.js:117:12)
at WarrantiesHomeButton (src/components/home/WarrantiesHomeButton/WarrantiesHomeButton.js:13:41)
at renderWithHooks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5762:18)
at updateFunctionComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7579:20)
at beginWork$1 (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9152:16)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 2 passed, 3 total
Snapshots: 1 passed, 1 total
Time: 4.846s, estimated 5s
Ran all test suites matching /WarrantiesHomeButton/i.
Watch Usage: Press w to show more.
【问题讨论】:
标签: reactjs react-native redux react-hooks