无法更新在React测试库中搜索到的待办事项?

huangapple go评论68阅读模式
英文:

Can't update the searched todos in the react testing library?

问题

程序运行正常,但当我尝试测试它时,它不能正常工作。我想在搜索后重新渲染待办事项,但是测试中的这部分似乎不能正常工作:我获取数据并将其设置为待办事项,搜索状态也在工作,因为当我记录它时,我可以看到该值,但是重新渲染没有触发,搜索的项目未显示,我仍然可以看到所有待办事项。我不知道该怎么做。我应该怎么做?

以下是代码:

useEffect(() => {
  fetch("some url todos")
    .then((response) => {
      return response.json();
    })
    .then((response) => {
      setTodos((todos) => ({ ...todos, all: response }));
    })
    .catch((e) => console.error(e));
}, []);

useEffect(() => {
  setTodos((todos) => ({
    ...todos,
    searched: search
      ? todos.all.filter((item) => {
          return item.title.toLowerCase().includes(search.toLowerCase());
        })
      : null,
  }));
}, [search]);

const handleOnChangeInput = (e: React.ChangeEvent<HTMLInputElement>) => {
  setSearch(e.target.value);
};

<div className="search-container">
  <input
    className="search"
    value={search}
    onChange={handleOnChangeInput}
    placeholder="Search todo..."
    data-testid="search"
    type="text"
  />
</div>
<div className="todos" data-testid="todos">
  {(todos.searched && todos.searched.length > 0
    ? todos.searched
    : todos.all
  ).map(({ title }) => (
    <p data-testid="todo">
      {title}
    </p>
  ))}
</div>

以下是测试的代码:

const mockResponse = [
  {
    userId: 1,
    id: 1,
    title: "Todo S",
    completed: false,
  },
  {
    userId: 1,
    id: 2,
    title: "Todo A",
    completed: true,
  },
];

beforeEach(() => {
  jest.spyOn(global, "fetch" as any).mockResolvedValue({
    json: () => mockResponse,
  });
});

afterEach(() => {
  jest.restoreAllMocks();
});

it("should filter todos based on search input", async () => {
  render(
    <MemoryRouter>
      <Home />
    </MemoryRouter>
  );

  const searchInput = screen.getByTestId("search");
  fireEvent.change(searchInput, {
    target: { value: "A" },
  });
  const todos = await screen.findAllByTestId("todo");
  expect(todos).toHaveLength(1);
});

希望这些翻译对您有帮助。

英文:

The program is working fine but when I try to test it does not work properly. I want to rerender the todos after they are searched but this part in the test seems to not work properly: I fetch the data and set it to the todos, also the search state is working because when I log it I can see the value, however, the rerender is not triggered and the searched items are not displayed, I can see still all the todos.
I don't know how to do it. What should I do?
here is the code:

useEffect(() =&gt; {
    fetch(&quot;some url todos&quot;)
      .then((response) =&gt; {
        return response.json();
      })
      .then((response) =&gt; { 
        setTodos((todos) =&gt; ({ ...todos, all: response }));
      })
      .catch((e) =&gt; console.error(e)); 
  }, []);

  useEffect(() =&gt; { 
    setTodos((todos) =&gt; ({
      ...todos,
      searched: search
        ? todos.all.filter((item) =&gt; { return item.title.toLowerCase().includes(search.toLowerCase());
          })
        : null,
    }));
  }, [search]);

  const handleOnChangeInput = (e: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
    setSearch(e.target.value);
  };
 &lt;div className=&quot;search-container&quot;&gt;
        &lt;input
          className=&quot;search&quot;
          value={search}
          onChange={handleOnChangeInput}
          placeholder=&quot;Search todo...&quot;
          data-testid=&quot;search&quot;
          type=&quot;text&quot;
        /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;todos&quot; data-testid=&quot;todos&quot;&gt;
        {(todos.searched &amp;&amp; todos.searched.length &gt; 0
          ? todos.searched
          : todos.all
        ).map(({title}) =&gt; (
          &lt;p
            data-testid=&quot;todo&quot;
          &gt;
            {title}
          &lt;/p&gt;
        ))}
      &lt;/div&gt;

Here are codes for the test:

const mockResponse = [
    {
      userId: 1,
      id: 1,
      title: &quot;Todo S&quot;,
      completed: false,
    },
    {
      userId: 1,
      id: 2,
      title: &quot;Todo A&quot;,
      completed: true,
    },
  ];

  beforeEach(() =&gt; {
    jest.spyOn(global, &quot;fetch&quot; as any).mockResolvedValue({
      json: () =&gt; mockResponse,
    });
  });

  afterEach(() =&gt; {
    jest.restoreAllMocks();
  });
it(&quot;should filter todos based on search input&quot;, async () =&gt; {
    render(
      &lt;MemoryRouter&gt;
        &lt;Home /&gt;
      &lt;/MemoryRouter&gt;
    );

    const searchInput = screen.getByTestId(&quot;search&quot;);
    fireEvent.change(searchInput, {
      target: { value: &quot;A&quot; },
    });
    const todos = await screen.findAllByTestId(&quot;todo&quot;);
    expect(todos).toHaveLength(1);
  });

答案1

得分: 1

因为您正在渲染组件并在渲染期间设置状态,所以需要使用waitFor异步实用函数等待UI完全渲染。

对我有效的一个好方法是在渲染后添加waitFor函数。

render(
  &lt;MemoryRouter&gt;
    &lt;Home /&gt;
  &lt;/MemoryRouter&gt;
);

await waitFor(async () =&gt; Promise&lt;void&gt;);

然后其余部分应该正常工作。

英文:

because you are rendering component and setting the state during rendering, you need to wait for the ui to be fully rendered using the waitFor async utitliy function.

a good technique that works for me.
adding waitFor fun after the render.

render(
  &lt;MemoryRouter&gt;
    &lt;Home /&gt;
  &lt;/MemoryRouter&gt;
);

await waitFor(async () =&gt; Promise&lt;void&gt;);

and the rest should works fine.

huangapple
  • 本文由 发表于 2023年6月19日 01:54:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76501880.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定