Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

renderHook: Context Provider, state change in child function act() error

未关闭
#1,206 5 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
3/5
预计耗时
1-2 天
新手友好度
25/100
Issue 类型
缺陷
描述清晰度
需要澄清
活跃度
停滞
技术栈
javascript, react

调研方向

从 useUserSearch hook、其 FetchUsers helper、StoreProvider 以及 issue 中展示的测试规范开始。使用模拟的 axios 响应运行 hook 测试,并跟踪异步状态更新;该 issue 没有定义所需的更改或明确的完成条件。

由索引模型根据 Issue 内容生成。

描述

question

I have a custom hook that uses a Context Provider for global state management. I am passing a dispatch and data from the state taken from the context inside the hook to a function that will at the end of requesting data from an endpoint dispatch this data coursing a state update inside the context API Provider passed down to the custom hook.

The issue is when I test the custom hook with a wrapper of the Context Provider passed into renderHook() function, I get in the test run the dreaded "act()" error of:

Warning: An update to StoreProvider inside a test was not wrapped in act(...)
      20 |      .then((res) => {
    > 22 |              setLoading(false)
         |              ^
      23 |              dispatch({ 
      24 |                      type: ActionTypesEnum.FETCH_USERS, 
      25 |                      payload: res.data

you can see the full explanation at: https://stackoverflow.com/questions/76157226/react-hooks-testing-library-context-provider-state-change-in-child-function-ac

The useUserSearch Hook:

const useUserSearch = (searchCriteria: string): SearchHookProps => {
  // data from the context and stored in the state
  const { dispatch, state } = useStateContext();
  const { users } = state;
  const [ userData, setUserData ] = useState<UserProps[]>([]);
  const [ loading, setLoading ] = useState<boolean>(false);
  const [ error, setError ] = useState<boolean>(false);
  // connected to the pagination affected by the search
  const [ currentPage, setCurrentPage ] = useState<number>(1)
  const [ totalPages, setTotalPages ] = useState<number>(1)
  const [ totalUserCount, setTotalUserCount ] = useState<number>(0)
  const totalPerPage = 12

  useEffect(() => {
    if (users === null) FetchUsers(dispatch, searchCriteria, setLoading, setError);
  }, [users]);

  useEffect(() => {
      if (searchCriteria.length > 0) FetchUsers(dispatch, searchCriteria, setLoading, setError);
  }, [searchCriteria]);

  return {
    userData,
    totalUserCount,
    setCurrentPage,
    currentPage,
    totalPages,
    loading,
    error,
  }
}

FetchUsers function:

const FetchUsers = (
	dispatch: Dispatch<any>,
	searchCriteria: string,
	setLoading: Dispatch<SetStateAction<boolean>>,
	setError: Dispatch<SetStateAction<boolean>>,) => {
	const { global: { apiUrl } } = AppConfig

	const search = `?q=${encodeURIComponent(searchCriteria)}`

	setLoading(true)
	axios.get(`${apiUrl}/users?q=${search}+in:login+type:user`)
	.then((res) => {
		setLoading(false)
		dispatch({ 
			type: ActionTypesEnum.FETCH_USERS, 
			payload: res.data
		})
	})
	.catch(() => {
		setLoading(false)
		setError(true)
	})
}

StoreProvider:

import React, { useContext, useReducer, useMemo, createContext } from 'react';
import Reducer from '../../Reducers';
import AppConfig from '../../Configs';
import { StateInterface, StoreInterface, ProviderInterface } from '../../Interfaces';

export const initialState: StateInterface = {
  appConfig: AppConfig,
  users: null,
};

export const StoreContext = createContext({
  state: initialState,
} as StoreInterface);

export const useStateContext = () => useContext(StoreContext);

export const StoreProvider = ({ children }: ProviderInterface) => {
  const [state, dispatch] = useReducer(Reducer, initialState);
  const StoreProviderValue = useMemo(() => ({ state, dispatch }), [state, dispatch]);

  return <StoreContext.Provider value={StoreProviderValue}>{children}</StoreContext.Provider>;
};

The Test spec file:

import React from 'react'
import { render, cleanup, waitFor, renderHook, act } from '@testing-library/react'
import axios from 'axios'

import usersMock from '../__mocks__/usersMock'
import { StoreProvider } from '../Providers'
import useUserSearch from './useUserSearch'

jest.mock('axios')
const mockedAxios = axios as jest.Mocked<typeof axios>;

describe('Test that <User />', () => {
  const renderHookComponent = (searchCriteria: string) => {
    const wrapper = ({ children }: any) => <StoreProvider>{children}</StoreProvider>
    const { result, rerender, unmount } = renderHook(() => useUserSearch(searchCriteria), {
      wrapper: wrapper as React.ComponentType<any>
    })
    return { result, rerender, unmount }
  }

  afterEach(() => {
    cleanup()
  })

  it('renders the hook useUserSearch', async () => {
    mockedAxios.get.mockResolvedValue(usersMock);
    const { result } = renderHookComponent('test')

    // await waitFor(() => {
    //   expect(result.current.userData.length).toBe(0)
    // })
  })
})
主要语言
JavaScript
星标
19.7k
派生
1.2k
PR 合并指标
30 天内没有已合并 PR

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

testing-library/react-testing-library 的其他 Issue

查看 testing-library/react-testing-library 的全部 Issue

相似的 Issue

更多 JavaScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。