Hacktoberfest 2026 : les issues que les mainteneurs ont marquées pour octobre, ouvertes et accessibles aux débutants. Parcourir les issues Hacktoberfest

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

Ouverte
#1,206 5 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Évaluation

Difficulté
3/5
Temps estimé
1-2 jours
Accessibilité débutants
25/100
Type d'issue
Bug
Clarté
À clarifier
Activité
À l'abandon
Stack technique
javascript, react
Domaine
frontend, testing-qa

Piste de recherche

Commencez par le hook useUserSearch, son helper FetchUsers, StoreProvider et la spécification de test présentée dans l’issue. Exécutez le test du hook avec la réponse axios simulée et suivez les mises à jour d’état asynchrones ; l’issue ne définit ni modification demandée ni condition d’achèvement claire.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

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)
    // })
  })
})
Langage dominant
JavaScript
Étoiles
19.7k
Forks
1.2k
Métriques de merge des PR
Aucune PR mergée en 30 j

Préparer son environnement

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de testing-library/react-testing-library

Toutes les issues de testing-library/react-testing-library

Issues similaires

Plus d'issues JavaScript

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.