Reactjs 使用useDispatch钩子时出现问题-警告:React检测到钩子顺序发生了更改

Reactjs 使用useDispatch钩子时出现问题-警告:React检测到钩子顺序发生了更改,reactjs,react-native,redux,react-hooks,Reactjs,React Native,Redux,React Hooks,这是我第一次在react.-native中使用Redux钩子,但我在测试中遇到了问题,因为当我运行测试时,我收到以下警告: 警告:React检测到调用的挂钩顺序发生了更改 打开主页按钮。这将导致错误和错误,如果不修复。 有关更多信息,请阅读挂钩规则: Previous render Next render ------------------------------------------------------ 1. useState

这是我第一次在react.-native中使用Redux钩子,但我在测试中遇到了问题,因为当我运行测试时,我收到以下警告:

警告:React检测到调用的挂钩顺序发生了更改 打开主页按钮。这将导致错误和错误,如果不修复。 有关更多信息,请阅读挂钩规则:

   Previous render            Next render
   ------------------------------------------------------
1. useState                   useContext
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
我不确定问题是我的代码还是我的测试方式,应用程序运行良好。如果我独立运行每个测试,测试将在没有警告的情况下通过,如果我运行所有三个测试,最后一个测试将抛出警告和错误

如果你们能给我指出我犯错误的正确方向,我将不胜感激

保证HomeButton.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 };
import React,{useState,useffect,useCallback}来自'React';
从“react-redux”导入{useSelector,useDispatch};
从“../WarriesHomeMenu”导入{WarriesHomeMenu};
从“../services”导入{userService};
从“../components”导入{HomeButton};
从“../constants”导入{screenNames};
从“../store”导入{userActions};
从“道具类型”导入道具类型;
常量保证HomeButton=({navigation})=>{
const[显示保修菜单,设置显示保修菜单]=使用状态(false);
const{carriesloginflow,user}=useSelector(
(state)=>state.userReducer,
);
const dispatch=usedpatch();
const handlepress=()=>{
如果(!userService.isAuthenticated()){
分派(userActions.warrancesloginstart());
导航。导航(屏幕名称。注册);
}否则{
设置显示保修菜单(true);
}
};
useffect(()=>{
验证ArrantiesFlow();
});
const validateWarrantiesFlow=useCallback(()=>{
if(保证登录流和用户id){
设置显示保修菜单(true);
分派(userActions.warrancesloginstop());
}
},[保修登录流,user.id,分派];
常量handleModalPress=()=>{
设置显示保修菜单(错误);
导航。导航(屏幕名称。服务中心);
};
const CLOSE保修主页菜单=()=>{
设置显示保修菜单(错误);
};
返回(
);
};
保修HomeButton.propTypes={
导航:PropTypes.objectOf(PropTypes.any),
};
导出{保修HomeButton};
保修HomeButton.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,
        );
      });
    });
  });
});
import*as React from'React';
从'react redux'导入{Provider};
进口{
渲染为rtlRender,
fireEvent,
等待
行动,
}来自“@testing library/react native”;
从“../services”导入{userService};
从“../constants”导入{screenNames};
从“./warranceshomebutton”导入{warranceshomebutton};
从'@auteco/store'导入{store};
jest.mock('@auteco/components',()=>require('@auteco/test').mockComponents());
jest.mock(“../WarriesHomeMenu”,()=>{
const{mockComponent}=require('@auteco/test');
返回{
保修HomeMenu:mockComponent(“保修HomeMenu”),
};
});
描述(“”,()=>{
让我们模仿道具;
const setState=jest.fn();
以前(()=>{
模拟道具={
导航:{
导航:jest.fn(),
危险的GetParent:jest.fn(()=>({state:null})),
},
};
jest.spyOn(userService,'isAuthenticated').mockReturnValue(true);
});
函数渲染(ui){
返回rtlRender({ui});
}
它('应该正确呈现',()=>{
const{baseElement}=render();
expect(baseElement.toMatchSnapshot();
});
description('并且用户经过身份验证',()=>{
描述('和按钮被按下',()=>{
它('应通过将SetShowArguresMenu状态设置为true来显示保修菜单',异步()=>{
jest.spyOn(React,'useState').mockReturnValue([false,setState]);
const{getByTestId}=render();
const-button=getByTestId('HomeButton');
button.props.onPress();
等待等待;
expect(setState).toBeCalledWith(true);
});
});
});
description('用户未通过身份验证',()=>{
描述('和按钮被按下',()=>{
它('应该导航到屏幕上方',异步()=>{
jest.spyOn(userService,'isAuthenticated').mockReturnValue(false);
jest.spyOn(React,'useState').mockReturnValue([false,setState]);
const{getByTestId}=render();
const-button=getByTestId('HomeButton');
行动(()=>{
button.props.onPress();
});
期望(mockProps.navigation.navigate).toBeCalledWith(
屏幕名称。注册,
);
});
});
});
});
控制台中的错误

  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.
console.error节点_modules/react native/Libraries/YellowBox/YellowBox.js:63
警告:React已检测到由保修HomeButton调用的挂钩顺序发生变化。这将导致错误和错误,如果不修复。有关更多信息,请阅读挂钩规则
上一次渲染下一次渲染
------------------------------------------------------
1.useState useContext
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
在主页按钮中
输入提供者
视图中(由视图创建)
视图中(由AppContainer创建)
视图中(由视图创建)
视图中(由AppContainer创建)
在AppContainer中(位于src/index.js:26)
console.error节点_modules/react native/Libraries/YellowBox/YellowBox.js:63
组件中发生了上述错误:
在主页按钮中
输入提供者
视图中(由视图创建)
视图中(由AppContainer创建)
视图中(由视图创建)
视图中(由AppContainer创建)
在AppContainer中(位于src/index.js:26)
考虑将错误边界添加到树中以自定义错误处理行为。
失败src/components/home/warranceshomebutton/warranceshomebutton.test.js
✓ 应正确渲染(55ms)
并对用户进行身份验证
和bu
   Previous render            Next render
   ------------------------------------------------------
1. useState                   useState
2. useState                   useState
3. useState                   useState
4. useState                   useState
5. useContext                 useEffect
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
let component: any;
await act(async () => {
  component = create(
    <MockedProvider>
      <SelectAccountScreen {...props} />
    </MockedProvider>,
  );
});