Here is the unit test solution:
app.jsx:
import React, { useState, useEffect } from 'react';
import { fetchData } from './api';
function App({ id }) {
  const [data, setData] = useState(null);
  const [isFetching, setIsFetching] = useState(false);
  const [error, setError] = useState(null);
  useEffect(() => {
    setIsFetching(true);
    fetchData(id)
      .then(
        (response) => {
          setData(response.data);
          setIsFetching(false);
        },
        (err) => {
          setError(err);
          setIsFetching(false);
        },
      )
      .catch((err) => {
        setError(err);
        setIsFetching(false);
      });
  }, [id]);
  if (data) {
    return <pre>{JSON.stringify(data)}</pre>;
  }
  if (isFetching) {
    return <div>fetching...</div>;
  }
  if (error) {
    return <pre>{JSON.stringify(error)}</pre>;
  }
  return <div>null</div>;
}
export default App;
api.js:
export function fetchData() {
  // real implementation
}
app.test.jsx:
import App from './app';
import { mount } from 'enzyme';
import { fetchData } from './api';
import { act } from 'react-dom/test-utils';
jest.mock('./api.js', () => {
  return {
    fetchData: jest.fn(),
  };
});
describe('59586141', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });
  it('should fetch data correctly', async () => {
    const mResponse = { data: { id: 1 } };
    const mProps = { id: 1 };
    fetchData.mockResolvedValueOnce(mResponse);
    const wrapper = mount(<App {...mProps}></App>);
    expect(wrapper.exists).toBeTruthy();
    expect(wrapper.find('div').text()).toBe('fetching...');
    await act(async () => {
      await new Promise((resolve) => setTimeout(resolve, 0));
    });
    wrapper.update();
    expect(wrapper.find('pre').text()).toBe(JSON.stringify(mResponse.data));
    expect(fetchData).toBeCalledWith(1);
  });
  it('should handle error if fetch data failure', async () => {
    const mError = new Error('some network error');
    const mProps = { id: 1 };
    fetchData.mockRejectedValueOnce(mError);
    const wrapper = mount(<App {...mProps}></App>);
    expect(wrapper.exists).toBeTruthy();
    expect(wrapper.find('div').text()).toBe('fetching...');
    await act(async () => {
      await new Promise((resolve) => setTimeout(resolve, 0));
    });
    wrapper.update();
    expect(wrapper.find('pre').text()).toBe(JSON.stringify(mError));
    expect(fetchData).toBeCalledWith(1);
  });
});
Unit test result with coverage report:
 PASS  src/stackoverflow/59586141/api.test.jsx (9.079s)
  59586141
    ✓ should fetch data correctly (119ms)
    ✓ should handle error if fetch data failure (12ms)
----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |     93.1 |      100 |       80 |     91.3 |                   |
 app.jsx  |     93.1 |      100 |       80 |     91.3 |             22,23 |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        10.458s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59586141