RTL doesn't throw invalid hook call warning

Viewed 86

From the Rules Of Hooks, we know React hook can NOT be called inside a condition. I have a custom react hook useRemoteData and I put it after the if(visible) return null statement. So it breaks the rules of hooks.

I expect that RTL should throw the invalid hook call warning. But it doesn't. Two test cases passed without any warning.

Eslint gives the correct warning message:

React Hook "useRemoteData" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?eslintreact-hooks/rules-of-hooks

fn.tsx:

import React from 'react';
import { useEffect, useState } from 'react';
import { api } from './api';

const useRemoteData = () => {
  const [state, setState] = useState('');
  useEffect(() => {
    api.ajax().then(() => setState('fake data'));
  }, []);
  return state;
};

interface TempCompProps {
  visible?: boolean;
}
export function TempComp({ visible = true }: TempCompProps) {
  if (!visible) return null;
  useRemoteData();
  return <div>TempComp</div>;
}

fn.test.tsx:

import { render, screen, waitFor } from '@testing-library/react';
import React from 'react';
import { TempComp } from './fn';

describe('TempComp', () => {
  test('should render component', async () => {
    render(<TempComp visible />);
    expect(await screen.findByText(/TempComp/)).toBeTruthy();
  });
  test('should not render component', () => {
    render(<TempComp visible={false} />);
    waitFor(() => {
      expect(screen.queryByText(/TempComp/)).toBeFalsy();
    });
  });
});

Test result:

  console.log
    simulate HTTP request

      at Object.<anonymous> (issues/component-visible-control/api.ts:3:13)

 PASS  issues/component-visible-control/fn.test.tsx
  TempComp
    ✓ should render component (52 ms)
    ✓ should not render component (3 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        3.426 s, estimated 5 s

package versions:

"@testing-library/react": "^12.1.2",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"jest": "^27.3.0",
1 Answers

This is actually fine for React if and only if the number of hook calls stays the same during the lifetime of the component. Since one test renders exactly zero hooks and another test with exactly one hook, no error will be thrown.

However, once you render the component with zero hooks and then one hook, you'll start to see the crash:

const {rerender} = render(<TempComp visible={false} />);
rerender(<TempComp visible={false} />);

Let me know if that makes sense

Related