useEffect 无限循环仅在测试时发生,否则不会发生 - 尽管使用 useReducer

useEffect infinite loop occurs only while testing, not otherwise - despite using useReducer

我正在尝试测试 useFetch 自定义挂钩。这是钩子:

import React from 'react';

function fetchReducer(state, action) {
  if (action.type === `fetch`) {
    return {
      ...state,
      loading: true,
    };
  } else if (action.type === `success`) {
    return {
      data: action.data,
      error: null,
      loading: false,
    };
  } else if (action.type === `error`) {
    return {
      ...state,
      error: action.error,
      loading: false,
    };
  } else {
    throw new Error(
      `Hello! This function doesn't support the action you're trying to do.`
    );
  }
}

export default function useFetch(url, options) {
  const [state, dispatch] = React.useReducer(fetchReducer, {
    data: null,
    error: null,
    loading: true,
  });

  React.useEffect(() => {
    dispatch({ type: 'fetch' });

    fetch(url, options)
      .then((response) => response.json())
      .then((data) => dispatch({ type: 'success', data }))
      .catch((error) => {
        dispatch({ type: 'error', error });
      });
  }, [url, options]);

  return {
    loading: state.loading,
    data: state.data,
    error: state.error,
  };
}

这是测试

import useFetch from "./useFetch";
import { renderHook } from "@testing-library/react-hooks";
import { server, rest } from "../mocks/server";

function getAPIbegin() {
  return renderHook(() =>
    useFetch(
      "http://fe-interview-api-dev.ap-southeast-2.elasticbeanstalk.com/api/begin",
      { method: "GET" },
      1
    )
  );
}

test("fetch should return the right data", async () => {
  const { result, waitForNextUpdate } = getAPIbegin();

  expect(result.current.loading).toBe(true);
  await waitForNextUpdate();
  expect(result.current.loading).toBe(false);
  const response = result.current.data.question;
  expect(response.answers[2]).toBe("i think so");
});

// Overwrite mock with failure case

test("shows server error if the request fails", async () => {
  server.use(
    rest.get(
      "http://fe-interview-api-dev.ap-southeast-2.elasticbeanstalk.com/api/begin",
      async (req, res, ctx) => {
        return res(ctx.status(500));
      }
    )
  );

  const { result, waitForNextUpdate } = getAPIbegin();

  expect(result.current.loading).toBe(true);
  expect(result.current.error).toBe(null);
  expect(result.current.data).toBe(null);
  await waitForNextUpdate();
  console.log(result.current);
  expect(result.current.loading).toBe(false);
  expect(result.current.error).not.toBe(null);
  expect(result.current.data).toBe(null);
});

我不知道如何解决这个问题。 URL 和选项必须在依赖项数组中,并且 运行ning useEffect 不会更改它们,所以我不明白为什么会导致此循环。当我将它们从数组中取出时,测试成功了,但是当这些事情发生变化时,我需要再次 运行 的效果。

有什么想法吗?

试试这个。

function getAPIbegin(url, options) {
  return renderHook(() =>
    useFetch(url, options)
  );
}

test("fetch should return the right data", async () => {
  const url = "http://fe-interview-api-dev.ap-southeast-2.elasticbeanstalk.com/api/begin";
  const options = { method: "GET" };
  const { result, waitForNextUpdate } = getAPIbegin(url, options);

  expect(result.current.loading).toBe(true);
  await waitForNextUpdate();
  expect(result.current.loading).toBe(false);
  const response = result.current.data.question;
  expect(response.answers[2]).toBe("i think so");
});

我没有使用过react-hooks-testing-library,但我的猜测是无论何时渲染React,都会重复调用发送到RenderHook的回调,导致每次传递不同的选项时间.